简体   繁体   English

在 TypeScript 中组合并集和交集类型时的参数类型解析

[英]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

Playground操场

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 of keyof 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM