属性 在联合类型上不存在

Property does not exist on type union

我已经通过 Whosebug 问题进行了搜索,但在我的案例中没有找到所需的答案。
我有

interface FruitBox {
  name: string
  desc: {
   'orange': number;
   'banana': number;
  }
}

interface IceBox {
  name: string
}

interface Vegabox {
  name: string
  desc: {
   'tomato': number;
   'potato': number;
  }
}

type UnionBox = FruitBox | Vegabox | IceBox;

我正在尝试实现什么

type Ship = Record<string, (description: UnionBox['desc'] | UnionBox['name']) => void>;

我正在尝试将 description 属性 分配给 UnionBox['desc'] 类型,如果 desc 参数不存在,则将其分配给 UnionBox['name']

收到下一个错误: Property 'desc' does not exist on type 'UnionBox'

很高兴得到任何帮助或提示

ps。我知道一定有一些奇怪的算法,只是不确定以哪种方式查看

您示例中的

UnionBox 可以是以下类型之一 - FruitBoxVegaboxIceBoxIceBox 是让 Typescript 不开心的地方——它不包含 属性 desc。想象一下,有一个 IceBox 类型的对象,然后将它传递给一个函数,该函数将 UnionBox 作为参数,然后使用 属性 desc - 它会访问一个非现有一个。

I'm trying to assign a description property to a type of UnionBox['desc'] and if desc parameter doesn't exist, then assign it to the UnionBox['name']

我不确定您希望它如何工作,因为在您的示例中描述只是简单地输入为字符串或字符串 - |运算符将简单地创建一个联合。但我想你可以简单地接受 unionBox: UnionBox 作为参数,并在其中添加一个逻辑,如果 desc 不存在,则 unionBox.desc ?? unionBox.name 将得到 name

看到这个。情况很相似。

当 TypeScript 解析联合类型时,它允许您使用 best common type。我的意思是所有联合元素通用的类型。因为这个选项是最安全的。

让我们检查一下您的工会:

interface FruitBox {
  name: string
  desc: {
   'orange': number;
   'banana': number;
  }
}

interface IceBox {
  name: string
}

interface Vegabox {
  name: string
  desc: {
   'tomato': number;
   'potato': number;
  }
}

type UnionBox = FruitBox | Vegabox | IceBox;

type AllowedKeys = keyof UnionBox // name

您可能已经注意到 (AllowedKeys),您只能使用 UnionBox 中的 name 属性。为什么 ?因为name存在于每个union中。

为了处理它,您可能需要使用 StrictUnion 助手。

// credits goes to 
type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> = 
    T extends any 
    ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;

type StrictUnion<T> = StrictUnionHelper<T, T>

完整代码:

interface FruitBox {
    name: string
    desc: {
        'orange': number;
        'banana': number;
    }
}

interface IceBox {
    name: string
}

interface Vegabox {
    name: string
    desc: {
        'tomato': number;
        'potato': number;
    }
}

type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> =
    T extends any
    ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;

type StrictUnion<T> = StrictUnionHelper<T, T>

type UnionBox = StrictUnion<FruitBox | Vegabox | IceBox>;

type AllowedKeys = keyof UnionBox // name


type Ship = Record<string, (description: UnionBox['desc'] | UnionBox['name']) => void>; // ok


const record: Ship = {
    foo: (elem) => { }
}

Playground