microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.93k stars 12.47k forks source link

`Tuple` type #50838

Closed mstephen19 closed 2 years ago

mstephen19 commented 2 years ago

The Tuple type

We want to create a type called Strings50 that is a tuple with 50 strings.

type Strings50 = [
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string,
    string
];

Welp, that's one way of doing it. Or, you could use this utility type I'm proposing:

type Tuple<T, N, R extends T[] = []> = R['length'] extends N ? R : Tuple<T, N, [...R, T]>;

With this recursive type, the abomination above can be refactored to this:

type Strings50 = Tuple<string, 50>;

Use case example

We have a function that takes in an array and a chunk size, then returns that same array chunked out:

const chunkArray = <T extends unknown>(arr: T[], size: number) => {
    const copy = [...arr];
    return [...Array(Math.ceil(arr.length / size))].map(() => copy.splice(0, size));
};

// x looks like -> [['hello', 'world'], ['foo', 'bar'], ['fizz', 'buzz']];
const x = chunkArray(['hello', 'world', 'foo', 'bar', 'fizz', 'buzz'], 2);

This is fine. The function returns T[][], and x is a string[][]. But, since for our use case, we intend to use non-dynamic chunk sizes when calling this function, we can make the return type more accurate:

const chunkArray = <T extends unknown, N extends number>(arr: T[], size: N) => {
    const copy = [...arr];
    return [...Array(Math.ceil(arr.length / size))].map(() => copy.splice(0, size)) as Tuple<T, N>[];
};

const x = chunkArray(['hello', 'world', 'foo', 'bar', 'fizz', 'buzz'], 2);
const y = chunkArray(['hello', 'world', 'foo', 'bar', 'fizz', 'buzz'], 3);

Instead of having the type string[][], x now has a type of [string, string][], and y of course has a type of [string, string, string][].

MartinJohns commented 2 years ago

See https://github.com/microsoft/TypeScript/issues/39522#issuecomment-656201388:

We've opted to not include utility type aliases in the lib unless they're required for declaration emit purposes.

Related: #47874