我正在尝试编写一个可以按如下方式使用的 Typescript 泛型类型:
/* All properties are nullable */
type A = {
a: string | null,
b: string | null;
}
type G_A = MyType<A>; // A | undefined
/* At least one property is not nullable */
type B = {
a: string;
b: string | null;
}
type G_B = MyType<B>; // B
因此的目标是,如果的所有属性都被允许,MyType<T>
它就返回,但如果不允许,它就只返回。T | undefined
T
null
T
我有点不知道该如何处理这个问题,所以我无法分享我尝试过的任何方法。
您可以使用这样的映射类型:
操场
解决这个问题的方法有很多种。其中一种方法是
由于
MyType<T>
是T
或T | undefined
,我们可以将其分解出来T
并将其放入具有类型(因为是)或 的并集中。never
T | never
T
undefined
我们尝试检查的是 的每个属性是否
T
为“可空”,我将其解释为“null
可以赋值”。这意味着对于每个属性 keyK
,我们都会检查 ifnull extends T[K]
,其中索引访问类型T[K]
是 key 处属性值的类型K
。我将其编写为分布式对象类型(在microsoft/TypeScript#47109中创造),其形式为
{[K in keyof T]: F<T[K]>}[keyof T]
,这是一种映射类型,我们可以立即使用所有键对其进行索引,以获取所有属性的联合。因此,{[K in keyof T]-?: null extends T[K] ? never : unknown}
它看起来像一个新的对象类型,其中每个属性值要么是never
(如果T[K]
可为空)要么是unknown
(如果它不可为空),具体取决于条件类型。请注意,never
是TypeScript 中的底层类型,并被吸收到所有联合中(X | never
isX
),而unknown
是顶层类型,并吸收所有联合(X | unkonwn
isunknown
)。请注意,我使用了-?
映射修饰符将可选属性转换为必需属性,因此undefined
不会显示多余的 。然后,
[keyof T]
我们得到并集。如果的所有属性T
均可空,则该映射类型的所有属性均为never
,因此并集为never
。如果的一个属性T
不可空,则该映射类型中至少有一个属性为unknown
,因此并集为unknown
。因此,分配对象类型要么是
never
(如果所有T
的属性均可为空),要么是unknown
(否则)。因此,我们将其与 进行检查never
。如果匹配never
,那么我们希望允许MyType<T>
包含undefined
其中,否则不允许。这就是它的工作原理。我们来测试一下:
这就是你想要的。注意,总会存在一些边缘情况,例如,如果你有索引签名、数组或元组类型,因为映射这些类型会产生特殊效果。你没有问到这些,所以它们超出了本文的范围,但重点是,你应该始终确保彻底测试任何使用泛型和条件类型构建的类型。
游乐场链接到代码