在 TypeScript 中组合联合和交集类型时的参数类型解析
Param type resolution when combining union and intersection types in TypeScript
这里有 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
以及 2 个简单的定义
const var1: T3 = { letter: 'b', base: 'low', valueFunc: (prop) => {}, valueType: 'empty' }
const var2: T3 = { letter: 'a', base: 'low', valueFunc: (prop) => {}, valueType: 0 }
这完全符合预期。 TS 正确评估了 valueFunc
中 prop
的类型。但是,如果我向 T2
添加另一个类型联合,TS 将不再能够解析 prop
但它仍然可以解析 valueType
.
修改类型
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 }
这是为什么?我错过了什么?
TypeScript 与 discriminated unions
配合得很好
T1
是有区别的,因为每个并集都有letter
属性,而T2
不是。您有两种方法可以解决此问题。
第一种方式
只需将 discriminator
添加到 T2
,例如:
type T2 = { type: '1', base: 'low' } | { type: '2', noise: 'high' }
第二种方式
使您的 T2
联合更加严格。请参阅此 以获得更多解释:
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
经验法则:
如果你有一个联合,其中每个对象都是不同的并且没有任何共同点 - 添加鉴别器。
更多解释
1)
Why we use UnionKeys
instead of keyof T
我们也可以使用T extends any
。这里的要点是对 distributivity. Why ? Because when you use keyof ({a:1}|{b:2})
you will get never
because they don't share common properties. See here.
使用条件类型
表示当您使用:
type UnionKeys<T> = T extends any ? keyof T : never;
keyof T
分别应用于联合中的每个元素而不是整个联合,因为我们在这里使用了 T extends any
.
一般来说,您应该将 T extends any
- 作为 turn on distributivity
和 [T] extends [any]
- 作为没有分配性的检查。
P.S。您可以查看我的 blog 以获得更多有趣的示例
这里有 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
以及 2 个简单的定义
const var1: T3 = { letter: 'b', base: 'low', valueFunc: (prop) => {}, valueType: 'empty' }
const var2: T3 = { letter: 'a', base: 'low', valueFunc: (prop) => {}, valueType: 0 }
这完全符合预期。 TS 正确评估了 valueFunc
中 prop
的类型。但是,如果我向 T2
添加另一个类型联合,TS 将不再能够解析 prop
但它仍然可以解析 valueType
.
修改类型
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 }
这是为什么?我错过了什么?
TypeScript 与 discriminated unions
配合得很好T1
是有区别的,因为每个并集都有letter
属性,而T2
不是。您有两种方法可以解决此问题。
第一种方式
只需将 discriminator
添加到 T2
,例如:
type T2 = { type: '1', base: 'low' } | { type: '2', noise: 'high' }
第二种方式
使您的 T2
联合更加严格。请参阅此
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
经验法则: 如果你有一个联合,其中每个对象都是不同的并且没有任何共同点 - 添加鉴别器。
更多解释
1)
Why we use
UnionKeys
instead ofkeyof T
我们也可以使用T extends any
。这里的要点是对 distributivity. Why ? Because when you use keyof ({a:1}|{b:2})
you will get never
because they don't share common properties. See here.
表示当您使用:
type UnionKeys<T> = T extends any ? keyof T : never;
keyof T
分别应用于联合中的每个元素而不是整个联合,因为我们在这里使用了 T extends any
.
一般来说,您应该将 T extends any
- 作为 turn on distributivity
和 [T] extends [any]
- 作为没有分配性的检查。
P.S。您可以查看我的 blog 以获得更多有趣的示例