[英]How to describe constraint using conditional type which is based on generic type?
I have a problem with typings in my code - union type constraint is generated instead of picking only one type from ItemProperties
types dictionary.我在代码中输入时遇到问题 - 生成了联合类型约束,而不是仅从
ItemProperties
类型字典中选择一种类型。 Is there any solution to make strict constraint for props
property inside ItemConfig
based on current ItemType
?是否有任何解决方案可以根据当前
ItemType
对ItemConfig
中的props
属性进行严格约束?
PS wrapping T
generic with tuple at props
type declaration (as said here ) not solves the problem. PS 在
props
类型声明中用元组包装T
generic(如此处所述)不能解决问题。
Simplified code example:简化代码示例:
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: ...
}
];
Expected typeof props
:预期
typeof props
:
{ name: string }
Actual typeof props
: typeof props
实际类型:
{ name: string } | { power: number } | {}
As remarked in the comments, the issue is that ItemConfig
is equal to {type: ItemType, props: {} | {name: string} | {power: number}}
如评论中所述,问题在于
ItemConfig
等于{type: ItemType, props: {} | {name: string} | {power: number}}
{type: ItemType, props: {} | {name: string} | {power: number}}
{type: ItemType, props: {} | {name: string} | {power: number}}
whereas you'll want it to be a discriminating union {type: ItemType.Dog, props: {name: string}} | {type: ItemType.Car, props: {power: number}} |..
{type: ItemType, props: {} | {name: string} | {power: number}}
而你会希望它成为一个有区别的联合{type: ItemType.Dog, props: {name: string}} | {type: ItemType.Car, props: {power: number}} |..
{type: ItemType.Dog, props: {name: string}} | {type: ItemType.Car, props: {power: number}} |..
to type the array element props
correctly. {type: ItemType.Dog, props: {name: string}} | {type: ItemType.Car, props: {power: number}} |..
正确输入数组元素props
。
One way to create this union is by using a distributive conditional type ( docs ):创建此联合的一种方法是使用分布式条件类型 ( docs ):
type ItemConfig<T = ItemType> = T extends ItemType ? {
type: T;
props: T extends keyof ItemProperties ? ItemProperties[T] : {};
} : never
TypeScript playground TypeScript 操场
Because the T
in the condition is a naked type parameter, ItemConfig<ItemType.Dog | ItemType.Car | ItemType.Building>
因为条件中的
T
是一个裸类型参数, ItemConfig<ItemType.Dog | ItemType.Car | ItemType.Building>
ItemConfig<ItemType.Dog | ItemType.Car | ItemType.Building>
ItemConfig<ItemType.Dog | ItemType.Car | ItemType.Building>
distributes to ItemConfig<ItemType.Dog> | ItemConfig<ItemType.Car> | ItemConfig<ItemType.Building>
ItemConfig<ItemType.Dog | ItemType.Car | ItemType.Building>
分发到ItemConfig<ItemType.Dog> | ItemConfig<ItemType.Car> | ItemConfig<ItemType.Building>
ItemConfig<ItemType.Dog> | ItemConfig<ItemType.Car> | ItemConfig<ItemType.Building>
ItemConfig<ItemType.Dog> | ItemConfig<ItemType.Car> | ItemConfig<ItemType.Building>
, which is the desired union. ItemConfig<ItemType.Dog> | ItemConfig<ItemType.Car> | ItemConfig<ItemType.Building>
,这是所需的联合。
Alternatively (as captain-yossarian remarked), since ItemType
extends PropertyKey
(ie string | number | symbol
), you can use a mapped type to create an object that has the constituents of the desired union as its values, and index that object to obtain the union:或者(如 captain-yossarian 所说),由于
ItemType
扩展了PropertyKey
(即string | number | symbol
),您可以使用映射类型创建一个 object ,它具有所需联合的成分作为其值,并索引该 object 以获得工会:
type ItemConfig = {
[T in ItemType]: {
type: T,
props: T extends keyof ItemProperties ? ItemProperties[T] : {}
}
}[ItemType]
TypeScript playground TypeScript 操场
This has the advantage that you don't need ItemConfig
to have a generic parameter T
, but it is limited to types that extend PropertyKey
(otherwise you can't use it as a key in the mapped type).这样做的好处是您不需要
ItemConfig
具有通用参数T
,但它仅限于扩展PropertyKey
的类型(否则您不能将其用作映射类型中的键)。
Both approaches approaches yield the same ItemConfig
union, which will allow the appropriate props
type to be inferred for each array element:这两种方法都会产生相同的
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}
}
];
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.