2
votes

Pourquoi le typographie ne peut pas déduire le type de retour lors de l'utilisation des vérifications de type de différenciation

Je veux comprendre pourquoi TypeScript ne peut pas déduire le type de retour de la fonction suivante (alors qu'il est capable de différencier dans l'instruction if-else):

function calc (arg: number|string) {
  return String(arg)
}

// infers test to be string
const test = calc(10)

Et quand on écrit la fonction suivante:

function calc (arg: number|string) {
  if (typeof arg === 'number') {
    // here typescript knows arg is number type
    return arg 
  } else if (typeof arg === 'string') {
    // here typescript knows arg is string type
    return arg 
  }
}

// infers test to be  number|string|undefined
const test = calc(10)

Ensuite, il peut bien sûr déduire le type de retour. Mais comment se fait-il qu'il ne puisse pas déduire le type de retour de la première fonction alors qu'il fournit une sécurité de type dans les différentes branches et qu'il sait qu'il obtient un type de nombre comme arg?

Éditer

Je sais comment le résoudre, mais je veux vraiment comprendre pourquoi TypeScript ne peut pas le faire?


4 commentaires

Utiliser une signature surchargée


Parce que le type inféré pour la première implémentation de calc est (arg: number | string) => number | string | undefined , il n'inférera pas comme par magie des surcharges ou des génériques.


Quelle est l' route que vous retournez dans l'exemple?


@paroxyzm erreur -> j'ai oublié de passer à arg


5 Réponses :


4
votes
function calc (arg: number): number;
function calc (arg: string): string;
function calc (arg: number|string) {
  if (typeof arg === 'number') {
    // here typescript knows arg is number type
    return route
  } else if (typeof arg === 'string') {
    // here typescript knows arg is string type
    return route
  }
}
You can specify what the return value is for a particular argument type by overloading the original function definition.See also: https://www.typescriptlang.org/docs/handbook/functions.html#overloads

0 commentaires

1
votes

si le type de retour est censé être le même que l'entrée, vous pouvez également utiliser Generic

function calc <T extends number|string>(arg: T): T {
  if (typeof arg === 'number') {
    // here typescript knows arg is number type
    return arg
  } else if (typeof arg === 'string') {
    // here typescript knows arg is string type
    return arg
  }
  throw new Error('should not reach here')
}

const t = calc(12)


1 commentaires

Il convient de noter qu'avec cette méthode, vos types d'entrée et de sortie doivent être symétriques.



3
votes

Typescript ne le fait pas automatiquement car cela nécessiterait d'évaluer votre fonction, c'est-à-dire d'exécuter réellement le code. Même si les paramètres sont connus au moment de la compilation et que cela pourrait théoriquement être possible, il n'essaye rien de ce genre. Peut-être que dans ce cas, il semble que cela devrait être simple, mais ce n'est pas vrai dans le cas général.

Quand il restreint le type pour vous à l'intérieur du bloc, cela signifie seulement que si le conditionnel est vrai, alors le type est X , mais il n'évalue pas réellement le conditionnel avec une valeur.


2 commentaires

Non, le compilateur peut voir que si number , renvoie un nombre, si une string , renvoie une chaîne, sinon pas de retour. Donc , il évalue le code.


@crashmstr TS peut voir l'ensemble de tous les chemins de code et vous indiquer le type que chacun renvoie, mais il ne se restreint pas à un chemin de code spécifique et ne déduit pas un type de retour basé sur les valeurs des arguments.



1
votes

La fonction que vous avez est essentiellement une fonction d'identité, elle retournera l'argument. Donc, si l'argument est de type X, le type de retour doit également être de type X (au moins).

Vous n'avez pas spécifié toutes les branches (bloc else ) donc théoriquement, cela pourrait également renvoyer undefined .

Par conséquent, il en déduit que le type global devrait être: X | undefined -> string | number | undefined

Que pourrait-il en déduire d'autre?

Dans le premier exemple, vous avez forcé la fonction à renvoyer String (arg), donc quels que soient les arguments, elle renverrait toujours une chaîne.


1 commentaires

Ouais, mais j'ai appelé la fonction avec un entier et un texte dactylographié attendu pour évaluer cela à travers la fonction, comme avec ces blocs, un seul résultat est possible maintenant. Cependant, si je comprends bien maintenant, dactylographié n'évalue pas réellement ces blocs.



1
votes

Dans le code

function calc3<T>(arg: T) {
    return arg 
}

let isAnInt = calc3(3);
let isAString = calc3('hello');

Il existe trois chemins dans le code:

  1. nombre
  2. chaîne
  3. autre chose qu'un nombre ou une chaîne

Dans le troisième cas, il n'y a pas de type de retour, donc la signature du retour est number | string | undefined

Vous pouvez voir cela si vous examinez le code dans le terrain de jeu TypeScript

function calc(arg: number | string): string | number | undefined
Tous les chemins de code ne renvoient pas une valeur. (7030)

Si vous voulez que votre variable "soit" le type unique basé sur l'argument passé, vous pouvez faire quelque chose comme ceci:

function calc (arg: number|string) {
  if (typeof arg === 'number') {
    // here typescript knows arg is number type
    return arg 
  } else if (typeof arg === 'string') {
    // here typescript knows arg is string type
    return arg 
  }
}

isAnInt est tapé comme un nombre et isAString est tapé comme une chaîne, et cela est basé sur le type passé dans la fonction.


0 commentaires