[英]Param type resolution when combining union and intersection types in TypeScript
Here are 3 simple types这里有3种简单的类型
type T1 =
| { letter: 'a'; valueFunc: (prop: number) => void; valueType: number }
| { letter: 'b'; valueFunc: (prop: string) => void; valueType: string }
type T2 = { base: 'low' }
type T3 = T1 & T2
And 2 simple definitions和 2 个简单的定义
const var1: T3 = { letter: 'b', base: 'low', valueFunc: (prop) => {}, valueType: 'empty' }
const var2: T3 = { letter: 'a', base: 'low', valueFunc: (prop) => {}, valueType: 0 }
This works perfectly as expected.这可以按预期完美运行。 TS correctly evaluates the types of
prop
in the valueFunc
. TS 正确评估
valueFunc
中的prop
类型。 However, if I add another type union to T2
, TS is no longer able to resolve prop
but it can still resolve valueType
.但是,如果我向
T2
添加另一个类型联合,则 TS 不再能够解析prop
但它仍然可以解析valueType
。
Modified types修改类型
type T1 =
| { letter: 'a'; valueFunc: (prop: number) => void; valueType: number }
| { letter: 'b'; valueFunc: (prop: string) => void; valueType: string }
type T2 = { base: 'low' } | {noise: 'high'}
type T3 = T1 & T2
const var1: T3 = { letter: 'b', base: 'low', valueFunc: (prop) => {}, valueType: 'empty' }
const var2: T3 = { letter: 'a', noise: 'high', valueFunc: (prop) => {}, valueType: 0 }
Why is that?这是为什么? What am I missing?
我错过了什么?
TypeScript works very well with discriminated unions TypeScript 与有区别的联合工作得很好
T1
is discriminated, because each union has letter
property, whereas T2
is not. T1
是有区别的,因为每个联合都有letter
属性,而T2
没有。 You have two ways to resolve this issue.您有两种方法可以解决此问题。
First way Just add discriminator
to T2
, for example:第一种方式只需将
discriminator
器添加到T2
,例如:
type T2 = { type: '1', base: 'low' } | { type: '2', noise: 'high' }
Second way Make your T2
union more strict.第二种方式使您的
T2
联合更加严格。 See this answer for more explanation:有关更多说明,请参阅此答案:
type T1 =
| { letter: 'a'; valueFunc: (prop: number) => void; }
| { letter: 'b'; valueFunc: (prop: string) => void; }
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 T2 = StrictUnion<{ base: 'low' } | { noise: 'high' }>
type T3 = T1 & T2
const var1: T3 = { letter: 'b', base: 'low', valueFunc: (prop) => { } } // prop is string
const var2: T3 = { letter: 'a', noise: 'high', valueFunc: (prop) => { } } // prop is number
Rule of thumb: If you have a union where each object is different and has nothing in common - add discriminator.经验法则:如果您有一个联合,其中每个对象都不同并且没有任何共同点 - 添加鉴别器。
More explanation更多解释
1) 1)
Why we use
UnionKeys
instead ofkeyof T
为什么我们使用
UnionKeys
而不是keyof T
We can use T extends any
as well.我们也可以使用
T extends any
。 The main point here it to use conditional typings for distributivity .这里的要点是使用条件类型进行分配。 Why ?
为什么 ? Because when you use
keyof ({a:1}|{b:2})
you will get never
because they don't share common properties.因为当您使用
keyof ({a:1}|{b:2})
时,您将never
不会得到,因为它们不共享公共属性。 See here .见这里。
It means that when you are using:这意味着当您使用时:
type UnionKeys<T> = T extends any ? keyof T : never;
keyof T
is applied to each element in a union separately and not to whole union only because we have used here T extends any
. keyof T
单独应用于联合中的每个元素,而不是仅应用于整个联合,因为我们在这里使用了T extends any
。
In general you should treat T extends any
- as turn on distributivity
and [T] extends [any]
- as check it without distributivity.一般来说,您应该将
T extends any
- 作为turn on distributivity
性和[T] extends [any]
- 作为检查它而不进行分配性。
PS You can check my blog for more interesting examples PS您可以查看我的博客以获取更多有趣的示例
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.