TypeScript:从可区分的联合创建新对象而不进行强制转换

tic*_*tic 1 casting object unions typescript

链接到以下Q的TS Playground

假设我有以下类型和变量声明:

interface TypeA {
    id: string;
    type: 'A',
    fields: {
        thing1: string;
        thing2: number;
    }
}

const defaultA = {
    thing1: 'hey',
    thing2: 123
}

interface TypeB {
    id: string;
    type: 'B',
    fields: {
        thing3: boolean;
        thing4: number;
    }
}

const defaultB = {
    thing3: true,
    thing4: 456
}

const defaultFields = {
    A: defaultA,
    B: defaultB,
}

type AnyType = TypeA | TypeB
Run Code Online (Sandbox Code Playgroud)

尝试创建一个新的AnyType通过:

const createNewThing = (type: TypeA['type'] | TypeB['type']): AnyType => {
    return {
        id: 'new id',
        type,
        fields: defaultFields[type],
    }
}
Run Code Online (Sandbox Code Playgroud)

抛出 TS 错误:

Type '{ id: string; type: "A" | "B"; fields: { thing1: string; thing2: number; } | { thing3: boolean; thing4: number; }; }' is not assignable to type 'AnyType'.
  Type '{ id: string; type: "A" | "B"; fields: { thing1: string; thing2: number; } | { thing3: boolean; thing4: number; }; }' is not assignable to type 'TypeB'.
    Types of property 'type' are incompatible.
      Type '"A" | "B"' is not assignable to type '"B"'.
        Type '"A"' is not assignable to type '"B"'.(2322)
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,我可以将正在创建的对象转换为AnyType

Type '{ id: string; type: "A" | "B"; fields: { thing1: string; thing2: number; } | { thing3: boolean; thing4: number; }; }' is not assignable to type 'AnyType'.
  Type '{ id: string; type: "A" | "B"; fields: { thing1: string; thing2: number; } | { thing3: boolean; thing4: number; }; }' is not assignable to type 'TypeB'.
    Types of property 'type' are incompatible.
      Type '"A" | "B"' is not assignable to type '"B"'.
        Type '"A"' is not assignable to type '"B"'.(2322)
Run Code Online (Sandbox Code Playgroud)

但如果可能的话,我宁愿避免选角。除了通过铸造之外,还有其他方法可以解决这个问题吗?

She*_* Yu 5

这实际上是打字稿的限制:可区分联合仅适用于顶级字段。

在您发布的示例中,thing1 thing2 thing3 thing4全部嵌套在fields. 如果您将它们提升了一个级别,您的示例应该会起作用。

有关更多信息,请参阅此博文