如何编写一个TypeScript类型实用程序来缩小对象中的联合类型?



我有一堆生成的类型(通过GraphQL代码),看起来像这样:

type X = {
a?: Maybe<number>
b?: Maybe<Array<MyType> | MyType>
c?: Maybe<MyType2>
d?: Maybe<Array<MyType3> | MyType3>
}
// Maybe is:
// type Maybe<T> = T | null | undefined;

字段bd,代码根做正确的事情根据GraphQL规范(见此问题),但在我的特定代码库,我需要转换所有类型基本上是这样的:

type X2 = {
a?: Maybe<number>
b?: Maybe<Array<MyType>>
c?: Maybe<MyType2>
d?: Maybe<Array<MyType3>>
}

如何编写类型的实用程序这样做吗?

(手动,将这样做:)

type X2 = Omit<X, 'b' | 'd'> & {
b?: Maybe<Array<MyType>>;
d?: Maybe<Array<MyType3>>;
};

将联合成员彼此分开有点棘手,我对一般用例不是100%确定,但这里有一种方法可以实现它:

type Xform<T> = {
[K in keyof T]: T[K] extends Maybe<infer V> ?
Extract<V, Array<any>>[number] extends infer R ?
T[K] extends Maybe<Array<R> | R> ?
Maybe<Array<R>>
: T[K] : T[K] : T[K]
}
type X2 = Xform<X>;
/* type X2 = {
a?: Maybe<number>;
b?: Maybe<MyType[]>;
c?: Maybe<MyType2>;
d?: Maybe<MyType3[]>;
} */

基本上我正在做的是:对于T的键中的每个键K,我正在检查属性值T[K]。如果我的任何测试失败,我将返回属性值不变的T[K]。我正在执行的测试:对于某些V,T[K]是否可分配给Maybe<V>?如果是这样,找到任何属于Array<any>的联合V成员,获取它们的元素类型,并将其保存到一个新的类型参数R中(为了方便)。最后:T[K]是否可分配给Maybe<Array<R> | R>?如果是,则返回Maybe<Array<R>>。所以我们正在寻找形式Maybe<Array<XYZ> | XYZ>的属性,并将它们转换为Maybe<Array<XYZ>>,否则我们就不管它们了。

总是有可能出现边缘情况;如果任何类型MyTypeXYZ本身是数组,或者如果你有其他东西的联合(例如,Maybe<MyType[] | MyType2>),但希望这至少给你一些方向。

Playground链接到代码

最新更新