我有带有属性的对象。这些属性可以是单值的,也可以是列表值的,而某些属性是可选的,这意味着它们可能是未定义的。因此,这里有一些示例对象:
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;
正确的类型定义看起来如何?
答案 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[]