Typescript indique que array.pop() peut renvoyer undefined même lorsqu'il est garanti que le tableau contient des éléments
Pourquoi Typescript m'avertit-il que cela pop()
pourrait revenir undefined
même après avoir vérifié que je supprime un élément d'un tableau non vide?
type Person = {
name: string;
};
const people: Person[] = [{ name: 'Sandra' }];
if (people.length) {
const { name } = people.pop();
// TS Error: Property 'name' does not exist on type 'Person | undefined'.
}
Même si je rends la garantie plus explicite, j'obtiens la même erreur:
if (people.length > 0 && people[people.length - 1]!== undefined){
const { name } = people.pop();
// TS Error: Property 'name' does not exist on type 'Person | undefined'.
}
Solution du problème
Voir microsoft/TypeScript#30406 pour une réponse canonique.
Pour la question "pourquoi le compilateur pense-t-il que cela pop()
pourrait revenir undefined
même lorsque je vérifie le tableau ", la réponse courte est "parce que la signature d'appellength
de la bibliothèque standard TypeScript pour la méthode sur les retours ":pop()
Array<T>
T | undefined
interface Array<T> {
//... elided
pop(): T | undefined;
//... elided
}
Ainsi, chaque fois que vous appelez pop()
un tableau, le type de la valeur de retour inclura undefined
.
La prochaine question logique est "pourquoi ne font-ils pas de meilleures signatures d'appel qui retournent T
si le length
est différent de zéro et undefined
si le length
est zéro?" La réponse à cela est "parce que la vérification de la length
propriété d'un type de tableau général ne change pas le type apparent du tableau, donc les signatures d'appel ne peuvent pas faire la différence".
Vous pouvez, par exemple, ajouter quelques signatures d'appel comme ceci :
interface Array<T> {
pop(this: { length: 0 }): undefined;
pop(this: { 0: T }): T;
}
By using this
parameters, each call signature will only be selected when the array on which the method is called matches the specified type. If the array has a length
of 0
, return undefined
. If the array has an element of type T
at the key 0
, return T
.
Cela fonctionnera bien pour les types de tuple dont la longueur est fixe et dont les indices d'élément sont connus :
declare const u: [];
const a = u.pop(); // undefined
declare const v: [1, 2];
const b = v.pop(); // 1 | 2
Mais bien sûr, appeler pop()
un tuple est une mauvaise idée et ce n'est pas le genre de chose que vous voudriez probablement. Si vous avez un tableau de longueur variable et que vous l'appelez pop()
, aucune signature d'appel n'est sélectionnée et vous revenez à la valeur intégrée T | undefined
, même si vous essayez de vérifier le length
:
const w = Math.random() < 0.5? []: ["a"] // string[]
if (w.length) {
w.pop().toUpperCase(); // error! Object is possibly undefined
}
Le problème est que la length
propriété de an Array<T>
est de type number
et qu'il n'y a pas de type "non nul number
" auquel se limiter w.length
. Afin de prendre en charge ce genre de chose, vous auriez besoin de quelque chose comme des types niés which are not part of TypeScript. It's possible that with sufficient compiler work, someone could give enough structure to arrays in TypeScript that a truthiness check on w.length
would narrow down the type of the array to something you can call pop()
on without worrying about getting undefined
out.
Mais cela ajouterait une tonne de complexité au compilateur et au langage pour prendre en charge ce cas d'utilisation. Il est peu probable que l'avantage l'emporte sur le coût.
In the absence of this, it's so much easier for you to skip the length
check and just call pop()
, checking to see whether it's undefined
or not. This is the same amount of work for you, since it's just moving the check from before to after the call, and it makes things more straightforward for the compiler.
Les autres réponses publiées ici suggèrent ceci et d'autres solutions de contournement, donc je ne les aborderai pas. Mon point principal est que le langage n'est tout simplement pas équipé pour permettre à une length
vérification d'affecter le comportement de pop()
. Ou, comme @RyanCavanaugh (chef de développement de TS) commenté dans microsoft/TypeScript#30406,
Ce n'est pas quelque chose que nous sommes capables de suivre
Tant pis!
Lien de l'aire de jeux vers le code
Commentaires
Enregistrer un commentaire