Skip to content

Instantly share code, notes, and snippets.

@seanf
Forked from jcalz/tuple.md
Last active April 29, 2018 23:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seanf/b056a40a7256efee10f53c82e490b5b3 to your computer and use it in GitHub Desktop.
Save seanf/b056a40a7256efee10f53c82e490b5b3 to your computer and use it in GitHub Desktop.
TypeScript tuple inference

You can use the tuple() function in tuple.ts to infer tuple types in TypeScript and cut down on the need to repeat yourself. Without tuple(), declaring a constant of a tuple type looks like this:

const daysOfTheWeek: ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"] = 
  ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];

You can't do this:

const daysOfTheWeek = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"]; 
  // right value, wrong type

because TypeScript would infer the type of daysOfTheWeek to be a plain array of string values.

And you can't do this:

const daysOfTheWeek: ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"]; 
  // right type, wrong value

because then the variable daysOfTheWeek would not be defined at runtime.

Enter tuple():

import { tuple } from './tuple'; // or wherever you put your libraries
const daysOfTheWeek = 
  tuple("sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"); // right type, right value

If you inspect the type of daysOfTheWeek, it will be the desired tuple type ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"], and has the correct value at runtime.


Note that the inferred tuple type will be as narrow as it can, interpreting any string, number, or boolean literal element as the corresponding literal type:

const narrow = tuple("a", 1, true); // inferred as type ["a", 1, true]

If you want to widen any of them, you can do type assertions:

const wider = tuple("a" as string, 1 as 0 | 1 | 2, true as boolean | undefined) 
// inferred as [string, 0 | 1 | 2, boolean | undefined]

Also note that the library only supports tuples of length up to twelve; you can add more overloads at the top if you need them.

/* tslint:disable:max-line-length */
// TypeScript wizardry thanks to jcalz:
// https://stackoverflow.com/a/45486495/14379
// https://gist.github.com/jcalz/381562d282ebaa9b41217d1b31e2c211
// Readonly added by seanf: https://gist.github.com/seanf/b056a40a7256efee10f53c82e490b5b3
export type Lit = string | number | boolean | undefined | null | void | {};
// infers a tuple type for up to twelve values (add more here if you need them)
export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit, F extends Lit, G extends Lit, H extends Lit, I extends Lit, J extends Lit, K extends Lit, L extends Lit>(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L): [A, B, C, D, E, F, G, H, I, J, K, L] & Readonly<[A, B, C, D, E, F, G, H, I, J, K, L]>;
export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit, F extends Lit, G extends Lit, H extends Lit, I extends Lit, J extends Lit, K extends Lit>(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K): [A, B, C, D, E, F, G, H, I, J, K] & Readonly<[A, B, C, D, E, F, G, H, I, J, K]>;
export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit, F extends Lit, G extends Lit, H extends Lit, I extends Lit, J extends Lit>(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J): [A, B, C, D, E, F, G, H, I, J] & Readonly<[A, B, C, D, E, F, G, H, I, J]>;
export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit, F extends Lit, G extends Lit, H extends Lit, I extends Lit>(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I): [A, B, C, D, E, F, G, H, I] & Readonly<[A, B, C, D, E, F, G, H, I]>;
export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit, F extends Lit, G extends Lit, H extends Lit>(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H): [A, B, C, D, E, F, G, H] & Readonly<[A, B, C, D, E, F, G, H]>;
export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit, F extends Lit, G extends Lit>(a: A, b: B, c: C, d: D, e: E, f: F, g: G): [A, B, C, D, E, F, G] & Readonly<[A, B, C, D, E, F, G]>;
export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit, F extends Lit>(a: A, b: B, c: C, d: D, e: E, f: F): [A, B, C, D, E, F] & Readonly<[A, B, C, D, E, F]>;
export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit>(a: A, b: B, c: C, d: D, e: E): [A, B, C, D, E] & Readonly<[A, B, C, D, E]>;
export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit>(a: A, b: B, c: C, d: D): [A, B, C, D] & Readonly<[A, B, C, D]>;
export function tuple<A extends Lit, B extends Lit, C extends Lit>(a: A, b: B, c: C): [A, B, C] & Readonly<[A, B, C]>;
export function tuple<A extends Lit, B extends Lit>(a: A, b: B): [A, B] & Readonly<[A, B]>;
export function tuple<A extends Lit>(a: A): [A] & Readonly<[A]>;
export function tuple(...args: any[]): Readonly<any[]> {
return args;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment