Мы создаем сопоставленный тип, который исключает свойства типа Function
. У нашего подхода есть проблема: он также удаляет необязательный ( ?
) Декоратор из сопоставленных свойств.
Вот упрощенное воспроизведение этого поведения. NoOpMap1
ведет себя так, как мы хотим, а NoOpMap2
имеет проблемное поведение.
type NoOpMap1<T> = { // Good. This one does not remove the ? [K in keyof T]: T[K]; }; type Keys<T> = { [K in keyof T]: K; }[keyof T]; type NoOpMap2<T> = { // Problem. This one removes the ? [K in Keys<T>]: T[K]; };
демонстрация
type SomeType = { foo?: string, } // type SomeTypeNoOpMap1 = { foo?: string; } type SomeTypeNoOpMap1 = NoOpMap1<SomeType>; // type SomeTypeNoOpMap2 = { foo: string; } type SomeTypeNoOpMap2 = NoOpMap2<SomeType>;
NoOpMap1
ведет себя как ожидалось. Это держит ?
Decorater на собственности foo
. NoOpMap2
удаляет его.
Почему NoOpMap2
удаляет ?
декоратор? Как мы можем достичь аналогичного результата, не удаляя его?
Вот полный тип, который мы пытаемся создать:
type DataPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]; type DataPropertiesOnly<T> = { [K in DataPropertyNames<T>] : T[K] extends (string | number | boolean) ? T[K] : T[K] extends (infer A)[] ? DataPropertiesOnly<A>[] : DataPropertiesOnly<T[K]>; };
Как уже упоминалось, вышеуказанный тип отвечает за удаление свойств типа Function
без удаления символа ?
декоратор из оставшихся свойств .
Всего 1 ответ
Если вы хотите сохранить необязательный / только для чтения статус свойств в отображаемом типе , вам необходимо убедиться, что компилятор воспринимает сопоставление как гомоморфное . Я знаю два способа сделать это.
{[K in keyof T]: ...}
отображение должно иметь вид {[K in keyof T]: ...}
где вы напрямую отображаете keyof T
для некоторого T
, общего или конкретного. Вы должны иметь что-то вроде in keyof
появляющееся непосредственно в типе, иначе это не будет учитываться.
interface Foo {
optional?: string;
readonly viewonly: string;
}
type Homomorphic = { [K in keyof Foo]: 0 };
// type Homomorphic = {
// optional?: 0 | undefined;
// readonly viewonly: 0;
// }
type KeyOf<T> = keyof T
type NonHomomorphic = { [K in KeyOf<Foo>]: 0 };
// type NonHomomorphic = {
// optional: 0;
// viewonly: 0;
// }
Другой способ сделать это состоит в том, чтобы отобразить параметр универсального типа K
который был ограничен keyof T
для другого параметра универсального типа T
Так:
type GenericConstraint<T, K extends keyof T> = { [P in K]: 0 };
type ConstrainedHomomorphic = GenericConstraint<Foo, keyof Foo>;
// type ConstrainedHomomorphic = {
// optional?: 0 | undefined;
// readonly viewonly: 0;
// }
type OnlySomeKeysStillHomomorphic = GenericConstraint<Foo, "viewonly">;
// type OnlySomeKeysStillHomomorphic = {
// readonly viewonly: 0;
// }
Этот последний метод был специально добавлен для получения гомоморфных частичных отображаемых типов, таких как Pick<T, K>
. И именно этот метод вам нужен, чтобы ваш фактический вариант использования работал:
// unchanged
type DataPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
// quick abort if T is a function or primitive
// otherwise pass to a homomorphic helper type
type DataPropertiesOnly<T> =
T extends Function ? never :
T extends object ? DPO<T, DataPropertyNames<T>> :
T
// homomorphic helper type
type DPO<T, KT extends keyof T> = {
[K in KT]
: T[K] extends (string | number | boolean) ? T[K]
: T[K] extends (infer A)[] ? DataPropertiesOnly<A>[]
: DataPropertiesOnly<T[K]>;
}
Я думаю, что будет действовать так, как вы хотите. Хорошо, надеюсь, это поможет; удачи!