TypeScript 中泛型的深度条件类型

Deep Conditional Type of a Generic in TypeScript

所以我有这个辅助函数,允许我根据结构匹配替换类型:

type Replace<T, C, A> = {
    [P in keyof T]: T[P] extends C ? A : T[P]
}

这允许我执行以下操作:

type NumberThing = { value: number }
type StringThing = Replace<NumberThing, number, string>

const a: StringThing = { value: "cenas" }

一切都很好,但有人这样做:

type ArrayOfNumberThing = { value: Array<number> }

好的,所以,我只是添加一个新条件...

type Replace<T, C, A> = {
    [P in keyof T]: T[P] extends C ? A : (T[P] extends Array<C> ? Array<A> : T[P])
}

然后输入:

type ArrayOfNumberThing = { value: Array<number>, simpleValue: number }
type ArrayOfStringThing = Replace<ArrayOfNumberThing, number, string>
const b: ArrayOfStringThing = { value: ["cenas"], simpleValue: "still works" }

但是这家伙很固执,现在扔给我一个:

type CrazyNumberThing = { value: Array<Array<Array<number>>> }

好吧,我总能做到这一点:

type RecursiveArrayReplace<T, C, A> = T extends C ? A : (T extends Array<infer E> ? RecursiveArrayReplace<E, C, A> : T)

... 显然,这会深入搜索数组,直到找到要替换的内容,对吗?正确的?错误:

Type alias 'RecursiveArrayReplace' circularly references itself.

在我擦干眼泪之前,有人给我扔了一个:

type TupleStringNumberThing = { value: [string, number] }

...这让我蜷缩在胎儿的位置,除非你们帮助我:(

鉴于你的具体例子,我会这样写:

type DeepReplace<T, C, A> = T extends C ? A : T extends object ? {
    [P in keyof T]: DeepReplace<T[P], C, A> } : T;

这应该适用于所有类型 including arrays/tuples if you're using TS3.1 or above:

type NumberThing = { value: number }
type StringThing = DeepReplace<NumberThing, number, string>
// type StringThing = { value: string; }

type ArrayOfNumberThing = { value: Array<number>, simpleValue: number }
type ArrayOfStringThing = DeepReplace<ArrayOfNumberThing, number, string>;
// type ArrayOfStringThing = { value: string[]; simpleValue: string; }

type CrazyNumberThing = { value: Array<Array<Array<number>>> };
type CrazyStringThing = DeepReplace<CrazyNumberThing, number, string>;
// type CrazyStringThing = { value: string[][][]; }

type TupleStringNumberThing = { value: [string, number] };
type TupleStringStringThing = DeepReplace<TupleStringNumberThing, number, string>;
// type TupleStringStringThing = { value: [string, string]; }

显然您可以找到其他边缘情况... accept/return C 的函数应该与 accept/return A 的函数一起使用吗?这些可能可以处理,但如果您的用例不需要它,那么额外的复杂性可能不值得。

好的,希望对您有所帮助;祝你好运!

Playground link to code