Глубокий условный тип Generi c в TypeScript - PullRequest
1 голос
/ 23 апреля 2020

Итак, у меня есть эта вспомогательная функция, которая позволяет мне заменять типы на основе структурного соответствия:

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] }

... что заставляет меня свернуться в позе зародыша, если вы, ребята, помоги мне: (

1 Ответ

3 голосов
/ 23 апреля 2020

Учитывая ваши конкретные примеры, я бы написал это так:

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

Это должно работать для всех типов , включая массивы / кортежи, если вы используете TS3.1 или выше :

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]; }

Очевидно, вы могли бы найти другие крайние случаи ... должны ли функции, которые принимают / возвращать C, быть с функциями, которые принимают / возвращают A? Скорее всего, они могут быть обработаны, но это может не стоить дополнительной сложности, если ваш вариант использования не нуждается в этом.

Хорошо, надеюсь, это поможет; удачи!

Детская площадка ссылка на код

...