有多种可能的方法来实现这一点.我在这里提供的解决方案涉及key-remapping in mapped types,它允许您转换输入类型的键和值.我们将首先计算一个名为Fields<T>
的中间类型,它将您的输入类型T
转换为一个包含键值tuple types的大union,然后我们将对其结果进行FilterFor<T>
运算,以获得最终类型.(感谢@Oblosys采用了这种方法).
所以Fields<{a: string, b: number}>
大约是["a", string] | ["a__search", string] | ["a__in", string[]] | ["b", number] | ["b__gt", number] | ⋯
,然后FilterFor<{a: string, b: number}>
会从Fields<{a: string, b: number}>
映射到{a: string, a__search: string, a__in: string[], b: number, b__gt: number, ⋯ }
.
以下是我如何写出Fields<T>
:
type Fields<T> = { [K in string & keyof T]:
T[K] extends number | Date ? [K | `${K}__${"gt" | "gte" | "lt" | "lte"}`, T[K]] :
T[K] extends object ? [K, string | T[K]] | [`${K}__in`, (string | T[K])[]] :
T[K] extends string ? [K | `${K}__search`, string] | [`${K}__in`, string[]] :
[K, T[K]]
}[string & keyof T]
这是表单{[K in KK]: F<K>}[KK]
的distributive object type,它最终为KK
个并集中的每个K
计算一个F<K>
的并集.在上面的代码中,KK
是string & keyof T
,或者只是输入类型T
的string
个键(这不包括number
个键和symbol
个键,如果它们存在的话).F<K>
是一系列conditional types,它们构成了K
的正确的键值元组.
我遵循的特殊规则是...首先在键K
处判断属性T
的值类型T[K]
.
如果它是number
或Date
,那么我们需要一串键,包括K
和带有各种比较后缀的K
的template-literal-type串联,值类型保持不变.
否则,如果它是object
(请注意,Date
是object
,所以我们需要按照这个顺序),那么我们希望保持键K
和值T[K]
与string
的联合,并且我们还希望键K
具有__in
后缀,并且值array为T[K]
-与-string
联合.
否则,如果它是string
,那么我们希望后缀为-__search
的K
和K
的值为string
,而后缀为-__in
的K
的值为string[]
.
否则,只需保留键K
和值T[K]
不变.
一旦我们做到了这一点,FilterFor<T>
就是:
type FilterFor<T> = { [F in Fields<T> as F[0]]: F[1] }
其仅使用第一个元素as
的键和第二个元素作为值来映射Field<T>
中的每个条目.
好的,让我们测试一下:
interface Foo {
id: string;
bar: string;
}
interface Person {
name: string;
age: number;
birthdate: Date;
active: boolean;
foo: Foo;
}
type FilterForPerson = FilterFor<Person>
/* type FilterForPerson = {
name: string;
name__search: string;
name__in: string[];
age: number;
age__gt: number;
age__gte: number;
age__lt: number;
age__lte: number;
birthdate: Date;
birthdate__gt: Date;
birthdate__gte: Date;
birthdate__lt: Date;
birthdate__lte: Date;
active: boolean;
foo: string | Foo;
foo__in: (string | Foo)[];
} */
看上go 不错!age
和birthdate
属性得到number
或Date
的处理,foo
属性得到非Date
到object
的处理,name
属性得到string
处理,active
得到一切处理.
Playground link to code个