microsoft / microsoft/TypeScript
for-of loop with intersection of array types produces a union of element types
Personne n'a encore pris cette issue.
- Langage dominant
- Go
- Étoiles
- 111k
- Forks
- 14.3k
- Merge moyen
- 1 j 19 h
- PR mergées (30 j)
- 117
Description
TypeScript Version: 3.9.2, 4.0.0-beta
Search Terms: intersection, for-of, union, iterable, iterator, iterate, array
Expected behavior:
When you use a for..of loop to iterate over the elements of an intersection of arrays (or maybe other iterables), what type should the elements be? I would expect either:
- you get the same type as when you index into the array: an intersection of the element types; or
- you get the same type as when you use the iterator method manually: the first element type because the iterator methods are overloads.
Actual behavior:
Iterating over an intersection of arrays with a for..of loop produces a union of their element types for some reason.
Related Issues:
#11961: intersection of array types results in overloaded methods (this would maybe imply overloaded iterators, but that's not happening here)
Aside:
I'm not sure why you'd want an intersection of array types in the first place; but the behavior showed up in a Stack Overflow question and I'm at a loss understanding why we get a union here.
Code
declare const arr: Array<{ a: string }> & Array<{ b: number }>;
for (const elemItr of arr) {
// { a: string } | {b : number } 😕
elemItr.a.toUpperCase(); // error!
elemItr.b.toFixed(); // error!
}
// I expected either this (intersection of element types)
const elemIdx = arr[0]; // { a: string; } & { b: number; }
elemIdx.a.toUpperCase(); // okay
elemIdx.b.toFixed(); // okay
// or this (overloaded iterators giving the first element type only)
const iter = arr[Symbol.iterator];
/* const iter: {
(): IterableIterator<{ a: string }>;
(): IterableIterator<{ a: string }>;
} & {
(): IterableIterator<{ b: number }>;
(): IterableIterator<{ b: number }>;
} */
const result = arr[Symbol.iterator]().next();
if (!result.done) {
result.value.a.toUpperCase(); // okay
result.value.b.toFixed(); // error, expected this
}
Output
"use strict";
for (const elemItr of arr) {
// { a: string } | {b : number } 😕
elemItr.a.toUpperCase(); // error!
elemItr.b.toFixed(); // error!
}
// I expected either this (intersection of element types)
const elemIdx = arr[0]; // { a: string; } & { b: number; }
elemIdx.a.toUpperCase(); // okay
elemIdx.b.toFixed(); // okay
// or this (overloaded iterators giving the first element type only)
const iter = arr[Symbol.iterator];
/* const iter: {
(): IterableIterator<{ a: string }>;
(): IterableIterator<{ a: string }>;
} & {
(): IterableIterator<{ b: number }>;
(): IterableIterator<{ b: number }>;
} */
const result = arr[Symbol.iterator]().next();
if (!result.done) {
result.value.a.toUpperCase(); // okay
result.value.b.toFixed(); // error, expected this
}
Compiler Options
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"useDefineForClassFields": false,
"alwaysStrict": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"downlevelIteration": false,
"noEmitHelpers": false,
"noLib": false,
"noStrictGenericChecks": false,
"noUnusedLocals": false,
"noUnusedParameters": false,
"esModuleInterop": true,
"preserveConstEnums": false,
"removeComments": false,
"skipLibCheck": false,
"checkJs": false,
"allowJs": false,
"declaration": true,
"experimentalDecorators": false,
"emitDecoratorMetadata": false,
"target": "ES2017",
"module": "ESNext"
}
}
Playground Link: Provided
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Piste de recherche
Commencez par le lien TypeScript Playground fourni et réduisez l’exemple d’intersection de tableaux afin de comparer l’itération for-of, l’accès indexé et un appel explicite à Symbol.iterator. Déterminez quel type d’élément for-of devrait produire, puis vérifiez le comportement choisi par rapport à la sortie du compilateur signalée et ajoutez une couverture pour le reproducer.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- typescript
- Domaine
- compilers
- Type d'issue
- Bug
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Activité
- À l'abandon
- Clarté
- Plutôt claire
- Accessibilité débutants
- 35/100