web-dev-qa-db-fra.com

Comment implémenter le type mappé partiel profond TypeScript ne cassant pas les propriétés du tableau

Avez-vous des idées sur la façon d'appliquer le type de mappage partiel de TypeScript à une interface de manière récursive, tout en ne cassant aucune clé avec les types de retour de tableau?

Les approches suivantes n'ont pas suffi:

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

Des idées?

MISE À JOUR: Réponse acceptée - Une solution meilleure et plus générale pour l'instant.

Avait trouvé une solution temporaire qui implique l'intersection des types et deux types mappés comme suit. L'inconvénient le plus notable est que vous devez fournir les remplacements de propriété pour restaurer les clés souillées, celles avec des types de retour de tableau.

Par exemple.

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

comme ça

23
Allan Simoyi

MISE À JOUR 2018-06-22:

Cette réponse a été écrite il y a un an, avant la sortie de l'incroyable fonctionnalité types conditionnels dans TypeScript 2.8. Cette réponse n'est donc plus nécessaire. Veuillez consulter @ krzysztof-kaczor nouvelle réponse ci-dessous pour savoir comment obtenir ce comportement dans TypeScript 2.8 et supérieur.


D'accord, voici ma meilleure tentative d'une solution folle mais entièrement générale (nécessitant TypeScript 2.4 et supérieur) qui pourrait ne pas en valoir la peine, mais si vous voulez l'utiliser, soyez mon invité:

Tout d'abord, nous avons besoin d'une logique booléenne au niveau du type:

type False = '0'
type True = '1'
type Bool = False | True
type IfElse<Cond extends Bool, Then, Else> = {'0': Else; '1': Then;}[Cond];

Tout ce que vous devez savoir ici, c'est que le type IfElse<True,A,B> correspond à A et IfElse<False,A,B> correspond à B.

Maintenant, nous définissons un type d'enregistrement Rec<K,V,X>, un objet avec la clé K et le type de valeur V, où Rec<K,V,True> signifie que la propriété est requise , et Rec<K,V,False> signifie que la propriété est facultative :

type Rec<K extends string, V, Required extends Bool> = IfElse<Required, Record<K, V>, Partial<Record<K, V>>>

À ce stade, nous pouvons accéder à vos types User et DeepPartialUser. Décrivons un général UserSchema<R> où chaque propriété qui nous tient à cœur est obligatoire ou facultative, selon que R est True ou 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>

Laid, non? Mais nous pouvons enfin décrire à la fois User et DeepPartialUser comme:

interface User extends UserSchema<True> { } // required
interface DeepPartialUser extends UserSchema<False> { }  // optional

Et voyez-le en action:

var user: User = {
  emailAddress: '[email protected]',
  verification: {
    verified: true,
    verificationCode: 'shazam'
  },
  activeApps: ['netflix','facebook','angrybirds']
} // any missing properties or extra will cause an error

var deepPartialUser: DeepPartialUser = {
  emailAddress: '[email protected]',
  verification: {
    verified: false
  }
} // missing properties are fine, extra will still error

Voilà. J'espère que ça t'as aidé!

10
jcalz

Avec TS 2.8 et les types conditionnels, nous pouvons simplement écrire:

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

Vous voudrez peut-être commander https://github.com/krzkaczor/ts-essentials package pour cela et certains autres types utiles.

56
Krzysztof Kaczor

Vous pouvez utiliser ts-toolbelt , il peut effectuer des opérations sur les types à n'importe quelle profondeur

Dans votre cas, ce serait:

import {O} from 'ts-toolbelt'

interface User {  
    emailAddress: string;  
    verification: {
      verified: boolean;
      verificationCode: string;
    }
    activeApps: string[];
}

type optional = O.Optional<User, keyof User, 'deep'>

Et si vous voulez le calculer en profondeur (à des fins d'affichage), vous pouvez utiliser Compute pour cela

1
pirix-gh