In TypeScript, what do the labels in the tuple type `[first: string, second?: number]` change, and what rule does the compiler enforce about labelling?
answer
- names for the reader, not the checker
- assignability is unchanged
- think parameter hints at call sites
- label every element or none
- erased like the rest of the type layer
basics
~20 sLabels are documentation for humans and tooling: they show in editor hints and become the parameter names when the tuple is used as a rest parameter list. They do not affect type identity or assignability. The rule is all-or-nothing — every element must be labelled, or none.
solid answer
~40 sLabelled tuple members name each position. The names show up in editor hints and error messages, and — the reason the feature exists — when the tuple is used as a rest parameter type, its labels become the displayed parameter names of that function. What they do **not** do is change the type: `[first: string, second?: number]` and `[string, number?]` are the same type, mutually assignable, and a differently-labelled tuple is assignable too. Labels are erased with everything else, so there is no runtime trace. The one hard rule is that labelling is all-or-nothing per tuple — mixing labelled and unlabelled elements is a compile error. Note the syntax details: the optional marker goes after the label as `second?: number`, and a rest element is written `...rest: boolean[]`.
code
typescript · 11 linestype Handler = [event: string, times?: number, ...flags: boolean[]];
declare function on(...args: Handler): void;
on('click');
on('click', 2, true);
// Labels carry no type identity: these are the same type.
const pair: [x: number, y: number] = [1, 2];
const renamed: [width: number, height: number] = pair;
console.log(renamed);go deeper
Recognise the syntax and know the labels are names for readers and editors, not a new kind of constraint on the value.
Explain that labels leave type identity and assignability untouched, that labelling is all-or-nothing per tuple, and that their real payoff is parameter names when the tuple types a rest parameter.
Push back when someone reaches for labels to enforce meaning — that needs distinct types or an object — and label tuples in published declaration files where call-site hints are what consumers actually read.
Set the convention: labelled tuples for exported signatures assembled from tuple types, named object properties wherever the distinction must be enforced rather than merely communicated.
## What a labelled tuple member is A tuple type normally lists bare types: `[string, number]`. TypeScript also lets you attach a name to each position: ```typescript type Coordinate = [x: number, y: number]; type Range = [start: number, end?: number]; type Args = [message: string, ...tags: string[]]; ``` The syntax deliberately mirrors a parameter list, and that is the clue to what it is for. ## What labels do — and do not — affect Labels are **purely documentary**. They participate in no type relationship: ```typescript type A = [x: number, y: number]; type B = [number, number]; type C = [width: number, height: number]; const a: A = [1, 2]; const b: B = a; // ok — same type const c: C = a; // ok — labels do not affect assignability ``` So you cannot use labels to prevent a width/height pair being passed where an x/y pair is expected. If you need the compiler to reject that, you need distinct element *types* — a branded or literal-tagged model, not a rename. What labels do affect is what a human sees. Hovering a value of type `Coordinate` shows `[x: number, y: number]`; error messages quote the labelled form; and completion for a call whose parameters come from a labelled tuple offers the real names. ## The reason the feature exists: rest parameters Before labels, typing a rest parameter with a tuple produced a signature whose parameters had no meaningful names — tooling showed placeholders like `args_0`, `args_1`. Labels fix exactly that: ```typescript declare function on(...args: [event: string, times?: number, ...flags: boolean[]]): void; on('click'); on('click', 2); on('click', 2, true, false); ``` The call site now sees `event`, `times` and `flags` in its hints, indistinguishable from a hand-written parameter list. That matters most in declaration files and in generic plumbing where a signature's parameters are assembled from a tuple type rather than written out. ## The all-or-nothing rule Within one tuple type, either every element carries a label or none does. Mixing them is a compile error, whose message is that tuple members must all have names or all not have names: ```typescript // type Bad = [first: string, number]; // Error: label some, label all type Good1 = [first: string, second: number]; type Good2 = [string, number]; ``` The rule exists because a half-labelled tuple reads ambiguously — `[first: string, number]` looks like it might be describing something other than a two-element list. ## Syntax details worth getting right The optional marker attaches to the **label**, not the type: `second?: number`, never `second: number?`. A rest element keeps its spread and takes an array type: `...tags: string[]`. And a label is not a binding — it does not create a variable, and destructuring is free to use entirely different names: ```typescript const r: [start: number, end: number] = [1, 9]; const [from, to] = r; // labels impose nothing on these names ``` ## Erasure Like the rest of the type layer, labels vanish at compile time. There is no metadata to reflect over and no way to read the label of a tuple position at runtime; if the names must exist in the emitted program, the value has to be an object with real properties, not a labelled tuple. ## Practical guidance Label a tuple whenever it is used as a rest-parameter type or is part of a published API surface — the cost is nothing and the readability gain at call sites is real. For a local two-element pair whose meaning is obvious from context, labels are optional noise. And when you catch yourself wanting labels to *enforce* something rather than to document it, that is the signal to switch to an object type, where property names are genuinely part of the type.
- Can labelling stop a width/height pair being passed where an x/y pair is expected?No. Labels are documentation and play no part in assignability, so `[width: number, height: number]` and `[x: number, y: number]` are interchangeable. To make the compiler object you need the element types themselves to differ — branded number types, or an object type with real property names, which are part of the type.
- Where does the optional marker go on a labelled element?After the label: `second?: number`. Writing `second: number?` is a syntax error. A rest element keeps the spread before the label, as `...tags: string[]`. The mental model that keeps this straight is that a labelled tuple element is written exactly like a function parameter.
saying these in an interview costs you the question
- Claims labels make two same-shaped tuples incompatible
- Thinks labels are readable at runtime
- Writes `second: number?` instead of `second?: number`
- Believes labels must match the destructured variable names
- Mixes labelled and unlabelled elements in one tuple