TypeScript:在不强制转换的情况下从有区别的联合创建一个新对象
TypeScript: Creating a new object from a discriminated union without casting
Link to TS playground of the following Q
假设我有以下类型和变量声明:
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
正在尝试通过以下方式创建新的 AnyType
:
const createNewThing = (type: TypeA['type'] | TypeB['type']): AnyType => {
return {
id: 'new id',
type,
fields: defaultFields[type],
}
}
抛出 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)
为了解决这个问题,我可以将正在创建的对象转换为 AnyType
:
const createNewThing = (type: TypeA['type'] | TypeB['type']): AnyType => {
return {
id: 'new id',
type,
fields: defaultFields[type],
} as AnyType;
}
但我宁愿尽可能避免转换。除了通过转换之外,还有其他方法可以解决这个问题吗?
这实际上是 typescript 的一个限制:discriminated unions 仅适用于顶级字段。
在您发布的示例中,thing1 thing2 thing3 thing4
都嵌套在 fields
下。如果您将它们提升了一个级别,您的示例应该有效。
有关更多信息,请参阅此 blog post。
Link to TS playground of the following Q
假设我有以下类型和变量声明:
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
正在尝试通过以下方式创建新的 AnyType
:
const createNewThing = (type: TypeA['type'] | TypeB['type']): AnyType => {
return {
id: 'new id',
type,
fields: defaultFields[type],
}
}
抛出 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)
为了解决这个问题,我可以将正在创建的对象转换为 AnyType
:
const createNewThing = (type: TypeA['type'] | TypeB['type']): AnyType => {
return {
id: 'new id',
type,
fields: defaultFields[type],
} as AnyType;
}
但我宁愿尽可能避免转换。除了通过转换之外,还有其他方法可以解决这个问题吗?
这实际上是 typescript 的一个限制:discriminated unions 仅适用于顶级字段。
在您发布的示例中,thing1 thing2 thing3 thing4
都嵌套在 fields
下。如果您将它们提升了一个级别,您的示例应该有效。
有关更多信息,请参阅此 blog post。