J'aimerais transformer un type en un type d'union discriminée:

type Current = {
  A : number,
  B : string,
}

À

type Target= {
  type: 'A',
  value: number
}
| {
  type: 'B',
  value: string
}

Pour que je puisse discriminer le résultat.

function handle(result: ToTarget<Current>) {
 switch(result.type){
  case 'A':
   return result.value // number
  case 'B':
   return result.value // string
 } 
}

Le plus proche que j'ai obtenu était:

type ToTargetA<U> = { code: keyof U, value : U[keyof U] } 
// code and value are not in sync so code is 'A | B' and value is 'number | string' but discriminating on code does not narrow value.

type ToTargetB<U, K extends keyof U = any> = { code: keyof K, value : U[K] }
// requires me to specify K as a generic parameter which is not what I want.

J'ai essayé plusieurs expressions de type conditionnel mais je n'ai pas pu me rapprocher de beaucoup.

0
nathanwinder 17 oct. 2020 à 03:53

2 réponses

Meilleure réponse

Voici une façon de procéder:

type ToDiscriminatedUnion<T, KK extends PropertyKey, VK extends PropertyKey> =
  { [K in keyof T]: { [P in KK | VK]: P extends KK ? K : T[K] } }[keyof T];

Vous pouvez vérifier qu'il produit le type souhaité:

type Target = ToDiscriminatedUnion<Current, 'type', 'value'>;
/* type Target = {
    type: "A";
    value: number;
} | {
    type: "B";
    value: string;
} */

La seule chose supplémentaire que j'ai faite là-bas a été de faire en sorte que vous puissiez spécifier les clés à donner aux noms de clé d'origine (K, étant T dans votre cas) et les noms de valeur d'origine ({{X2 }}, étant {A: {type: "A", value: number}, B: {type: "B". value: string}} dans votre cas). Si vous ne voulez jamais changer cela, vous pouvez le coder en dur:

Ensuite, nous pouvons regarder jusqu'à l'union de ses valeurs de propriété en l'indexant avec [keyof T], produisant l'union discriminée {type: "A", value: string} | {type: "B", value: number} souhaitée.


Propriétés de type aux types d'union discriminés

type ToDiscriminatedUnion<T> =
  { [K in keyof T]: { type: K, value: T[K] } }[keyof T];

type Target = ToDiscriminatedUnion<Current>;

Lien de l'aire de jeux vers le code

2
jcalz 17 oct. 2020 à 01:43

Cela fonctionne, mais je me demande s'il existe une solution plus propre / meilleure.

type DistributedProperties<T> = { [P in keyof T] : { code: P, value: T[P]} } 
type Union<T> = DistributedProperties<T>[keyof DistributedProperties<T>]
0
nathanwinder 17 oct. 2020 à 01:42