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?
Je sais comment le résoudre, mais je veux vraiment comprendre pourquoi TypeScript ne peut pas le faire?
5 Réponses :
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
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)
Il convient de noter qu'avec cette méthode, vos types d'entrée et de sortie doivent être symétriques.
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.
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.
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.
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.
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:
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 } }
Où 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.
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