microsoft / microsoft/TypeScript

Assume arity of tuples when declared as literal

Open
#24,350 13 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Revisit Suggestion
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

Search Terms

tuples, length, arity

Suggestion

Now that #17765 is out I'm curious about if we could change the arity of tuples declared as literals. This was proposed as part of #16896 but I thought it might be better to pull this part out to have a discussion about this part of that proposal.

With fixed length tuples TypeScript allows you to convert [number, number] to number[] but not the other way around (which is great).

const foo = [1, 2];
const bar = [1, 2] as [number, number];

const foo2: [number, number] = foo;
const bar2: number[] = bar;

If you declare a constant such as foo above it would be nice if it would be nice if it would have the length as part of its type, that is, if foo was assumed to be of type [number, number] not number[].

This would have potential issues with mutable arrays allowing you to call push and pop although this isn't different to present and was discussed a bit in #6229.

const foo = [1, 2] as [number, number]; // After this proposal TypeScript would infer the type as `[number, number]` not `number[]`.
foo.push(3); // foo is of type `[number, number]` even though it now has 3 elements.
foo.splice(2); // And now it has 2 elements again.

Use Cases

When using a function such as fromPairs from lodash it requires that the type is a list of tuples. A simplified version is

function fromPairs<T>(values: [PropertyKey, T][]): { [key: string]: T } {
  return values.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
}

If I do

const foo = fromPairs(Object.entries({ a: 1, b: 2 }));

it works because the type passed into fromPairs is [string, number][], but if I try to say map the values to double their value I get a compile error:

const bar = fromPairs(Object.entries({ a: 1, b: 2 }).map(([key, value]) => [key, value * 2]));

as the parameter is of type (string | number)[][]

This can be fixed by going

const bar = fromPairs(Object.entries({ a: 1, b: 2 }).map(([key, value]) => [key, value * 2] as [string, number]));

but this is cumbersome.

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript / JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. new expression-level syntax)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the tuple examples and use case in the issue, then read the discussions linked in #17765, #16896, and #6229. Define how literal tuple arity should interact with mutable operations such as push and pop; the work is done when the shown declarations and mapped Object.entries example infer the intended tuple types without assertions.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.