как реализовать TypeScript глубокий частичный сопоставленный тип, не нарушающий свойства массива
любые идеи о том, как можно применить частичный сопоставленный тип typescript к интерфейсу рекурсивно, в то же время не нарушая никаких ключей с типами возврата массива?
следующих подходов было недостаточно:
interface User {
emailAddress: string;
verification: {
verified: boolean;
verificationCode: string;
}
activeApps: string[];
}
type PartialUser = Partial<User>; // does not affect properties of verification
type PartialUser2 = DeepPartial<User>; // breaks activeApps' array return type;
export type DeepPartial<T> = {
[ P in keyof T ]?: DeepPartial<T[ P ]>;
}
какие идеи?
обновление: Принятый ответ-лучшее и более общее решение на данный момент.
нашел временное обходное решение, которое включает пересечение типов и двух сопоставленных типов следующим образом. Самые заметные недостатком является то, что вы должны предоставить переопределения свойств для восстановления запятнанных Ключей, с типами возврата массива.
Э. Г.
type PartialDeep<T> = {
[ P in keyof T ]?: PartialDeep<T[ P ]>;
}
type PartialRestoreArrays<K> = {
[ P in keyof K ]?: K[ P ];
}
export type DeepPartial<T, K> = PartialDeep<T> & PartialRestoreArrays<K>;
interface User {
emailAddress: string;
verification: {
verified: boolean;
verificationCode: string;
}
activeApps: string[];
}
export type AddDetailsPartialed = DeepPartial<User, {
activeApps?: string[];
}>
2 ответов
обновление 2018-06-22:
этот ответ был написан год назад, перед удивительными условных типов функция была выпущена в TypeScript 2.8. Поэтому этот ответ больше не нужен. Пожалуйста, см. @Кшиштоф-Качор это ответ ниже для способа получить это поведение в TypeScript 2.8 и выше.
хорошо, вот моя лучшая попытка сумасшедшего, но полностью общего решения (требующего TypeScript 2.4 и выше), которое может не стоить того для вас, но если вы хотите использовать его, пожалуйста:
во-первых, нам нужна логическая логика на уровне типа:
type False = '0'
type True = '1'
type Bool = False | True
type IfElse<Cond extends Bool, Then, Else> = {'0': Else; '1': Then;}[Cond];
все, что вам нужно знать, это то, что тип IfElse<True,A,B>
значение A
и IfElse<False,A,B>
оценивает в B
.
теперь определим тип записи Rec<K,V,X>
объект с ключом K
и значение типа V
, где Rec<K,V,True>
означает свойство требуются и Rec<K,V,False>
означает свойство дополнительно:
type Rec<K extends string, V, Required extends Bool> = IfElse<Required, Record<K, V>, Partial<Record<K, V>>>
на данный момент мы можем добраться до вашего User
и DeepPartialUser
типы. Опишем общие UserSchema<R>
где каждое свойство, о котором мы заботимся, является обязательным или необязательным, в зависимости от того,R
и True
или False
:
type UserSchema<R extends Bool> =
Rec<'emailAddress', string, R> &
Rec<'verification', (
Rec<'verified', boolean, R> &
Rec<'verificationCode', string, R>
), R> &
Rec<'activeApps', string[], R>
некрасиво, верно? Но мы можем, наконец, описать оба User
и DeepPartialUser
as:
interface User extends UserSchema<True> { } // required
interface DeepPartialUser extends UserSchema<False> { } // optional
и увидеть его в действии:
var user: User = {
emailAddress: 'foo@example.com',
verification: {
verified: true,
verificationCode: 'shazam'
},
activeApps: ['netflix','facebook','angrybirds']
} // any missing properties or extra will cause an error
var deepPartialUser: DeepPartialUser = {
emailAddress: 'bar@example.com',
verification: {
verified: false
}
} // missing properties are fine, extra will still error
там вы идете. Надеюсь, это поможет!
С TS 2.8 и условными типами мы можем просто написать:
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends Array<infer U>
? Array<DeepPartial<U>>
: T[P] extends ReadonlyArray<infer U>
? ReadonlyArray<DeepPartial<U>>
: DeepPartial<T[P]>
};
вы можете проверить https://github.com/krzkaczor/ts-essentials пакет для этого и некоторых других полезных типов.