1
votes

typescript: comment renvoyer un type approprié à partir d'une fonction générique contrainte par un type de recherche générique retournant une union

Disons que nous avons l'implémentation de fonction suivante:

// ✅$ExpectType {"WUT?" | { hello: 'Guten Tag!'; time?: undefined; } | { time: 'Wie spat is es?'; hello?: undefined; }}
const t1 = getUnion('ASK')

Le type de retour de cette fonction est l'union suivante:

{"WUT?" | {bonjour: 'Guten Tag!'; heure?: indéfini; } | {time: 'Wie craché est es?'; bonjour?: indéfini; }}

Donc, ce que nous pouvons penser, c'est que si nous utilisons la contrainte de fonction par notre type générique par "union discriminante" utilisée dans le cas du commutateur, cela renverrait ce type de branche particulier, comme suit:

// 🚨 NOPE !
// $ExpectType  {time: 'Wie spat is es?'}
const t1 = getUnion('ASK')

Malheureusement, cette hypothèse est incorrecte car nous obtenons une union entière au lieu de ce type restreint

type Action = 'GREET' |'ASK'

function getUnion<T extends Action>(action: T) {
  switch (action) {
  case 'GREET':
    return {hello: 'Guten Tag!'} as const
  case 'ASK':
    return {time: 'Wie spat is es?'} as const
  default:
    return 'WUT?'
  }
}

Est-ce un comportement correct ou plutôt une limitation du compilateur?

Quoi qu'il en soit, comment cela peut-il être résolu?

Donc, const t1 = getUnion ('ASK') retournera {time: 'Wie craché est es? '} ?


0 commentaires

3 Réponses :


2
votes

Donc, ce que j'ai trouvé est le suivant:

Cette implémentation atténue le problème précédent, car elle retourne correctement le type rétréci de l'union de retour par l'argument de fonction utilisé, via le mappeur de types conditionnels:

// ✅ exactly what we wanted 
// $ExpectType {hello:'Guten Tag!'}
const t11 = getUnionStrict('ASK')

// ✅
// $ExpectType {time:'Wie spat is es?'}
const t22 = getUnionStrict('GREET')


1 commentaires

Il n'y a aucun moyen pour "WUT?" d'être renvoyé, non? Je ferais probablement un type comme interface ProperReturn {GREET: {hello: string}; DEMANDEZ: {time: string}} et faites en sorte que le type de retour soit ProperReturn [T] et ignorez à la fois le type conditionnel et l'impossible retour "WUT"? . Mais essentiellement, vous avez raison de dire que vous devez définir vous-même les conditions de type de retour, puis faire des assertions (ou une surcharge de signature d'appel unique, ce qui est souvent plus facile)



1
votes

Une option consiste à utiliser une interface de mappage au lieu d'utiliser un type conditionnel, ce qui rend le type de retour plus facile à suivre. De plus, j'utilise généralement une signature d'implémentation séparée avec les génériques et une signature d'implémentation qui n'est pas générique et renvoie une union. Bien que ce ne soit pas sûr de type à 100%, c'est mieux que la version d'assertion de type.

type Action = 'GREET' | 'ASK'
interface ProperReturn {
  'GREET': { hello: 'Guten Tag!' }
  'ASK': { time: 'Wie spat is es?' }
}
function getUnion<T extends Action>(action: T): ProperReturn[T]
function getUnion(action: Action): ProperReturn[keyof ProperReturn] {
  switch (action) {
    case 'GREET':
      return { hello: 'Guten Tag!' } as const
    case 'ASK':
      return { time: 'Wie spat is es?' } as const
    default:
      throw "WUT";
  }
}


1 commentaires

Oui, plus propre que ma solution proposée. marquer celui-ci comme solution. Salut les gens!



0
votes

Pour les chercheurs comme moi: j'ai trouvé un mélange des deux réponses, que je pense être à la fois sûr et plus propre:

type ProperReturn = {
  'GREET': { hello: 'Guten Tag!' }
  'ASK': { time: 'Wie spat is es?' }
}

// better than retyping 'GREET' | 'ASK'
type Action = keyof ProperReturn

function getUnion<T extends Action>(action: T): ProperReturn[T] {
  switch (action) {
  case 'GREET':
    return {hello: 'Guten Tag!' } as ProperReturn[T]
  case 'ASK':
    return {time:'Wie spat is es?'} as ProperReturn[T]
  default:
    throw "WUT?"
  }
}

// t1: { hello: 'Guten Tag!'; }
const t1 = getUnion('GREET')

// t2: { time: 'Wie spat is es?'; }
const t2 = getUnion('ASK')

lien Playground p> p>


0 commentaires