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

Typescript conditional type - implement a type changing function

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

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

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

编辑: 现在,我实现了一个函数,它采用这样一个对象和 属性 的名称以及 属性 的 return 的名称,但有一个例外:如果属性 不是可选的,也不是数组,return 是 属性 作为数组,原始类型是数组类型。

现在,我实现了一个函数,它接受这样一个对象和 属性 的名称以及 属性 的 return 的名称,但有一个例外:如果属性 不是一个数组,它 return 是 属性 作为一个原始类型为数组类型的数组。

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

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

现在我想将此行为放入接口中的 return 类型定义中,但我终究无法弄清楚如何让它发挥作用。

我最好的尝试是:

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;

这样的单个可选道具它会失败

正确的类型定义是什么样的?

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

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