如何使用基于泛型类型的条件类型来描述约束?
How to describe constraint using conditional type which is based on generic type?
我在代码中输入时遇到问题 - 生成了联合类型约束,而不是仅从 ItemProperties
类型字典中选择一种类型。是否有任何解决方案可以根据当前 ItemType
在 ItemConfig
中对 props
属性 进行严格约束?
P. S.wrapping T
generic with tuple at props
type declaration (as said ) 不能解决问题。
简化代码示例:
enum ItemType {
Dog = 1,
Car = 2,
Building = 3,
}
interface ItemProperties {
[Item.Dog]: {
name: string;
};
[Item.Car]: {
power: number;
};
}
interface ItemConfig<T extends ItemType = ItemType> {
type: T;
props: T extends keyof ItemProperties ? ItemProperties[T] : {};
}
const config: ItemConfig[] = [
{
type: ItemType.Dog,
props: ...
}
];
预期 typeof props
:
{ name: string }
实际 typeof props
:
{ name: string } | { power: number } | {}
如评论中所述,问题是 ItemConfig
等于 {type: ItemType, props: {} | {name: string} | {power: number}}
而您希望它是一个区分联合 {type: ItemType.Dog, props: {name: string}} | {type: ItemType.Car, props: {power: number}} | ..
来键入数组元素 props
正确。
创建此联合的一种方法是使用分布式条件类型 (docs):
type ItemConfig<T = ItemType> = T extends ItemType ? {
type: T;
props: T extends keyof ItemProperties ? ItemProperties[T] : {};
} : never
因为条件中的T
是一个裸类型参数,所以ItemConfig<ItemType.Dog | ItemType.Car | ItemType.Building>
分配给ItemConfig<ItemType.Dog> | ItemConfig<ItemType.Car> | ItemConfig<ItemType.Building>
,也就是想要的并集
或者(如 captain-yossarian 所述),由于 ItemType
扩展了 PropertyKey
(即 string | number | symbol
),您可以使用映射类型创建一个对象,该对象具有所需联合的成分作为其值,并索引该对象以获得联合:
type ItemConfig = {
[T in ItemType]: {
type: T,
props: T extends keyof ItemProperties ? ItemProperties[T] : {}
}
}[ItemType]
这样做的好处是你不需要ItemConfig
有一个泛型参数T
,但它仅限于扩展PropertyKey
的类型(否则你不能将其用作映射类型中的键)。
两种方法都产生相同的 ItemConfig
联合,这将允许为每个数组元素推断出适当的 props
类型:
const config: ItemConfig[] = [
{
type: ItemType.Dog,
props: {name: 'x'}
// type of props: {name: string}
},
{
type: ItemType.Car,
props: {power: 7}
// type of props: {power: number}
},
{
type: ItemType.Dog,
props: {power: 7} // Type error
// type of props: {name: string}
}
];
我在代码中输入时遇到问题 - 生成了联合类型约束,而不是仅从 ItemProperties
类型字典中选择一种类型。是否有任何解决方案可以根据当前 ItemType
在 ItemConfig
中对 props
属性 进行严格约束?
P. S.wrapping T
generic with tuple at props
type declaration (as said
简化代码示例:
enum ItemType {
Dog = 1,
Car = 2,
Building = 3,
}
interface ItemProperties {
[Item.Dog]: {
name: string;
};
[Item.Car]: {
power: number;
};
}
interface ItemConfig<T extends ItemType = ItemType> {
type: T;
props: T extends keyof ItemProperties ? ItemProperties[T] : {};
}
const config: ItemConfig[] = [
{
type: ItemType.Dog,
props: ...
}
];
预期 typeof props
:
{ name: string }
实际 typeof props
:
{ name: string } | { power: number } | {}
如评论中所述,问题是 ItemConfig
等于 {type: ItemType, props: {} | {name: string} | {power: number}}
而您希望它是一个区分联合 {type: ItemType.Dog, props: {name: string}} | {type: ItemType.Car, props: {power: number}} | ..
来键入数组元素 props
正确。
创建此联合的一种方法是使用分布式条件类型 (docs):
type ItemConfig<T = ItemType> = T extends ItemType ? {
type: T;
props: T extends keyof ItemProperties ? ItemProperties[T] : {};
} : never
因为条件中的T
是一个裸类型参数,所以ItemConfig<ItemType.Dog | ItemType.Car | ItemType.Building>
分配给ItemConfig<ItemType.Dog> | ItemConfig<ItemType.Car> | ItemConfig<ItemType.Building>
,也就是想要的并集
或者(如 captain-yossarian 所述),由于 ItemType
扩展了 PropertyKey
(即 string | number | symbol
),您可以使用映射类型创建一个对象,该对象具有所需联合的成分作为其值,并索引该对象以获得联合:
type ItemConfig = {
[T in ItemType]: {
type: T,
props: T extends keyof ItemProperties ? ItemProperties[T] : {}
}
}[ItemType]
这样做的好处是你不需要ItemConfig
有一个泛型参数T
,但它仅限于扩展PropertyKey
的类型(否则你不能将其用作映射类型中的键)。
两种方法都产生相同的 ItemConfig
联合,这将允许为每个数组元素推断出适当的 props
类型:
const config: ItemConfig[] = [
{
type: ItemType.Dog,
props: {name: 'x'}
// type of props: {name: string}
},
{
type: ItemType.Car,
props: {power: 7}
// type of props: {power: number}
},
{
type: ItemType.Dog,
props: {power: 7} // Type error
// type of props: {name: string}
}
];