Typescript条件类型-实现类型更改功能

时间:2019-04-14 20:59:55

标签: typescript conditional-types

我有带有属性的对象。这些属性可以是单值的,也可以是列表值的,而某些属性是可选的,这意味着它们可能是未定义的。因此,这里有一些示例对象:

type A = {
   myProp: string;
   otherProp?: number;
}

type B = {
   coolProp?: boolean[];
   someProp: SomeOtherType[];
}

编辑: 现在,我实现了一个函数,该函数接受这样的对象和属性名称,并按原样返回该属性,但有一个例外:如果该属性不是可选的且不是数组,则它将该属性返回为原始类型为数组类型的数组。

现在,我实现了一个函数,该函数接受一个这样的对象和属性名称,并按原样返回该属性,但有一个例外:如果该属性不是数组,则将其作为带有数组的数组返回原始类型作为数组类型。

因此,例如,来自上方的类型B的属性不会更改,但是来自类型A的myProp: string;将变为:myProp: string[];otherProp?: number也将保持不变。

因此,例如,来自上方的类型B的属性不会更改,但是来自类型A的myProp: string;将变为:myProp: string[];otherProp?: number将成为otherProp?: number[]

现在,我想将此行为放入接口的返回类型定义中,但是我一生都无法弄清楚如何使其工作。

我最大的尝试是:

getPropFromObject<O, P extends keyof O>(node: O, propName: P):
    any[] extends O[P] ? O[P] : O[P] extends undefined ? O[P] : O[P][]

但对于诸如otherProp?: number;

这样的单个可选道具却失败

正确的类型定义看起来如何?

1 个答案:

答案 0 :(得分:0)

处理联合时的类型关系可能与您期望的相反。工会是其任何成员的基本类型。例如:

type N = number | undefined extends undefined ? "Y" : "N" //No, the union does not extend a member
type Y = undefined extends number | undefined  ? "Y" : "N" // Yes the union extends a member

起初这可能令人惊讶,但是如果您以集合的方式来考虑类型,那是有道理的。基本类型是一个包括代表子类型的所有集合的集合(所有子类型实例也应该是基本类型的实例)。

因此回到您的问题,如果要测试undefined是否与其他类型的属性联合,则需要编写:undefined extends O[P]

declare function getPropFromObject<O, P extends keyof O>(node: O, propName: P):
    any[] extends O[P] ? O[P] : undefined extends O[P] ? O[P] : O[P][]

type A = {
    myProp: string;
    otherProp?: number;
}

getPropFromObject(null as any as A, "otherProp") // number | undefined
getPropFromObject(null as any as A, "myProp") // string[]

修改

更改问题后,要获得所需的效果,可以使用distributive conditional type。这将分布在诸如number | undefined之类的联合中,并且条件类型将应用于联合的每个成员。

type ToArray<T> = T extends unknown ? T extends undefined ? T : T[] : never;
declare function getPropFromObject<O, P extends keyof O>(node: O, propName: P):
    any[] extends O[P] ? O[P] : ToArray<O[P]>

type A = {
    myProp: string;
    otherProp?: number;
}

getPropFromObject(null as any as A, "otherProp") // number[] | undefined
getPropFromObject(null as any as A, "myProp") // string[]