microsoft / microsoft/TypeScript

Tuples: maintaining type-param in the type of it's items

Ouverte
#61,750 3 commentaires 1 réaction 0 personnes assignées Voir sur GitHub
Experimentation Needed Suggestion
Langage dominant
Go
Étoiles
111k
Forks
14.3k
Merge moyen
2 j 4 h
PR mergées (30 j)
132

Description

### 🔍 Search Terms

for-of, T[number], tuple (I couldn't find more specific search terms).

### ✅ Viability Checklist

- [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
- [x] This wouldn't change the runtime behavior of existing JavaScript code
- [x] This could be implemented without emitting different JS based on the types of the expressions
- [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- [x] This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- [x] This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals

### ⭐ Suggestion

When a function takes an array/tuple as a type-parameter, e.g.

```ts
function foo(items: T): void;
```

When accessing the items of this array (`items[0]`), they are "resolved" to the type defined for the generic, e.g., `unknown` in this example, but should be kept unresolved so that they show up as `T[number] | undefined`.

This becomes an issue once a function takes more than one array like this, for example, if it wants to use the tuple/array shape as a param in the output. This can lead to real runtime errors where TypeScript isn't providing enough safety.

This is also relevant for for-of loops iterating over items

```ts
for (const item of items) {
console.log(item);
// ^? unknown, can be T[number]
}
```

### 📃 Motivating Example

The following example passes typescript checking but fails in runtime and throws exceptions when trying to access methods of strings on a number, and vice-versa.

```ts
type DoubleMap<
T extends readonly unknown[],
S extends readonly unknown[],
RT,
RS,
> = [...{ [I in keyof T]: RT }, ...{ [I in keyof S]: RS }];

function doubleMap<
T extends readonly unknown[],
S extends readonly unknown[],
RT,
RS,
>(
t: T,
s: S,
mapperT: (item: T[number]) => RT,
mapperS: (item: S[number]) => RS,
): DoubleMap {
const output: (T[number] | S[number])[] = [];

for (const itemT of t) {
// Oops, we got the mappers wrong, we are calling mapperS with an item from T
output.push(mapperS(itemT));
}

for (const itemS of s) {
// ...And here we are calling mapperT with an item from S
output.push(mapperT(itemS));
}

// @ts-expect-error [ts2322] -- TypeScript can't tell we finished building the output, this is fine...
return output;
}

const result = doubleMap(
// ^? [boolean, boolean, boolean, number, number, number]
["a", "b", "c"] as const,
[1, 2, 3] as const,
(item) => item.startsWith("a"),
(item) => item.toPrecision(1),
);
```

https://www.typescriptlang.org/play/?noUncheckedIndexedAccess=true&noUnusedLocals=true&noUnusedParameters=true&noPropertyAccessFromIndexSignature=true&noImplicitOverride=true&noFallthroughCasesInSwitch=true&exactOptionalPropertyTypes=true#code/C4TwDgpgBAIg9gVwEYBsIFkCGYA8AoKKAFSggA9gIA7AEwGcoAnCTGuKlEKBKgayrgB3KgG0AugBoCUAMqkK1ekxZsOXHvyGjJ0gEpEphXTKkA+KAF4oIgHR2A3tYCSUAJZUovCCDgAzYmIAXFD6UAC+ElB2No4iLu6e3n6yQSFyYWIA3Hh4vjwAxsCu7FBsyGhYuNIk5JS0DMys7JzcfALC4oay8nVKjaotGu3aXfqjJnimABTSwMEG0nTBE4QAttiQjETBU66Uq-MiVAirSBCMYgCUluZj0utgmzI7exAHskcnZxfXFrcTl2C8HKGGwOAMskiYzS5ns0ny7DowCgiGAYAQcygUyIn1O5zEUAAPh9jnifuJLNYsjlCL44IwsQiqEi3PsSMlgNc4YRCAB6XlQADycDAdEigmgAHM4MjgAALaAPTYMQSMdiS8XQTDMKD5TAoFDuSVQJXnOSCPZyqCYDyvVZQXxq+1EaSEVHo4A2dF0OVTU2MGS7NmXS7ZQhhGkO+mMxHIu1yZJ0LmuqD8qJ2ACCtCgCp1EutOr1BqNJo25xIFvl1tt+wdTtkKfdGK9CB9frLWyDbxkIbD4UjaYAAsA6ABaciQQrjxhqhkiEcAJgAzAuFwTR6PiOAIDJ8oxXGBkXqqAByWUQA1QfO+dyuH0QGhQJAIVwoGgl+XQJvASLyu9uBgbyoCBomkZhgAQRgPG-bIIzwJkWWYOgEBQZErDKVBQTAGY+QFKAAD0AH5pBEAAiTBSMiUikEoqBSPyUiCUwBgEJ-EiAEZIgXSIlyYljYy6LtVl+cw7RsJFtRHAB1S0pnI0jLkEu0RNZN4bGAOAAAVmHyO9iioKZ2MUvBQzwIA

Live example:

https://codesandbox.io/p/sandbox/tgr8pm

### 💻 Use Cases

1. What do you want to use this for?
2. What shortcomings exist with current approaches?
3. What workarounds are you using in the meantime?

Guide de contribution

Ouvrir le guide de contribution

Piste de recherche

Commencez par l’exemple motivant doubleMap et le TypeScript Playground associé, puis étudiez comment sont actuellement vérifiés l’accès indexé aux tuples génériques et l’itération for-of. Le travail est terminé lorsque les appels aux mappers inversés sont rejetés, tandis que les utilisations valides de tuples et de tableaux conservent les types d’élément appropriés T[number] ou S[number].

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
typescript
Domaine
compilers
Type d'issue
Fonctionnalité
Difficulté
5/5
Temps estimé
Plus d'une semaine
Activité
À l'abandon
Clarté
Plutôt claire
Accessibilité débutants
35/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.