rekursjon - Hvordan implementere TypeScript dyp delvis kartlagt type uten å bryte array-egenskaper
Noen ideer om hvordan man kan bruke TypeScripts Partial-mapped type på et grensesnitt rekursivt, samtidig som man ikke bryter noen nøkler med array-returtyper?
Følgende tilnærminger har ikke vært tilstrekkelige:
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 ]>;
}
Noen ideer?
OPPDATERING: Akseptert svar - En bedre og mer generell løsning for nå.
Had funnet en midlertidig løsning som involverer kryssing av typer og to kartlagte typer som følger. Den mest bemerkelsesverdige ulempen er at du må oppgi egenskapsendringene for å gjenopprette forurensede nøkler, de med array-returtyper.
F.eks.
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[];
}>
Slik
Svar #1
Med TS 2.8 og betingede typer kan vi enkelt skrive:
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]>
};
eller med [] i stedet for Array<> som ville være:
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends (infer U)[]
? DeepPartial<U>[]
: T[P] extends Readonly<infer U>[]
? Readonly<DeepPartial<U>>[]
: DeepPartial<T[P]>
};
Du vil kanskje sjekke ut https://github.com/krzkaczor/ts-essentials-pakken for dette og noen andre nyttige typer.
Svar #2
OPPDATERING 2018-06-22:
Dette svaret ble skrevet for et år siden, før den fantastiske funksjonen med betingede typer ble utgitt i TypeScript 2.8. Så dette svaret er ikke lenger nødvendig. Vennligst se @krzysztof-kaczor sitt nye svar nedenfor for måten å få dette atferd på i TypeScript 2.8 og nyere.
Okay, her er mitt beste forsøk på en sprø, men fullt generell løsning (krever TypeScript 2.4 og oppover) som kanskje ikke er verdt det for deg, men hvis du vil bruke den, vær så god:
Først trenger vi en viss type nivå boolsk logikk:
type False = '0'
type True = '1'
type Bool = False | True
type IfElse<Cond extends Bool, Then, Else> = {'0': Else; '1': Then;}[Cond];
Alt du trenger å vite her er at typen IfElse<True,A,B> evalueres til A og IfElse<False,A,B> evalueres til B.
Nå definerer vi en rekordtype Rec<K,V,X>, et objekt med nøkkel K og verditype V, hvor Rec<K,V,True> betyr at egenskapen er påkrevd, og Rec<K,V,False> betyr at egenskapen er valgfri:
type Rec<K extends string, V, Required extends Bool> = IfElse<Required, Record<K, V>, Partial<Record<K, V>>>
På dette punktet kan vi gå til dine User og DeepPartialUser-typer. La oss beskrive en generell UserSchema<R> der hver egenskap vi bryr oss om enten er påkrevd eller valgfri, avhengig av om R er True eller 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>
Stygt, ikke sant? Men vi kan endelig beskrive både User og DeepPartialUser som:
interface User extends UserSchema<True> { } // required
interface DeepPartialUser extends UserSchema<False> { } // optional
Og se det i aksjon:
var user: User = {
emailAddress: '[email ',
verification: {
verified: true,
verificationCode: 'shazam'
},
activeApps: ['netflix','facebook','angrybirds']
} // any missing properties or extra will cause an error
var deepPartialUser: DeepPartialUser = {
emailAddress: '[email ',
verification: {
verified: false
}
} // missing properties are fine, extra will still error
Der har du det. Håper det hjelper!
