Chapter 7: TypeScript Tuples
Part 1: What Exactly IS a Tuple?
A tuple is an array with a fixed number of elements, where each element has a specific type at a specific position. The order matters, the count matters, and the types at each position matter.
The Basic Syntax
|
0 1 2 3 4 5 6 7 8 9 10 |
<span class="token comment">// This is a tuple</span> <span class="token keyword">let</span> user<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"Alice"</span><span class="token punctuation">,</span> <span class="token number">30</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// This is NOT a tuple - it's just an array</span> <span class="token keyword">let</span> notATuple<span class="token operator">:</span> <span class="token punctuation">(</span><span class="token builtin">string</span> <span class="token operator">|</span> <span class="token builtin">number</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"Alice"</span><span class="token punctuation">,</span> <span class="token number">30</span><span class="token punctuation">]</span><span class="token punctuation">;</span> |
Key difference: The tuple says “position 0 MUST be string, position 1 MUST be number, and there are EXACTLY 2 positions.” The array says “this array can contain any mix of strings and numbers, in any order, of any length.”
Part 2: Creating and Using Tuples
Simple Tuples
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<span class="token comment">// A 2D coordinate</span> <span class="token keyword">let</span> point<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// A colored RGB value</span> <span class="token keyword">let</span> red<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">255</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// A key-value pair</span> <span class="token keyword">let</span> entry<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">any</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"username"</span><span class="token punctuation">,</span> <span class="token string">"alice123"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Reading values - TypeScript knows exact types</span> <span class="token keyword">let</span> x <span class="token operator">=</span> point<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Type: number</span> <span class="token keyword">let</span> y <span class="token operator">=</span> point<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Type: number</span> <span class="token comment">// Writing values - must match the type at that position</span> point<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">15</span><span class="token punctuation">;</span> <span class="token comment">// ✓ Works</span> point<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">25</span><span class="token punctuation">;</span> <span class="token comment">// ✓ Works</span> point<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">"15"</span><span class="token punctuation">;</span> <span class="token comment">// Error! Type 'string' is not assignable to type 'number'</span> |
TypeScript’s Memory Is Perfect
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<span class="token keyword">let</span> person<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">boolean</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"Bob"</span><span class="token punctuation">,</span> <span class="token number">25</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// TypeScript remembers the type of each position</span> <span class="token keyword">let</span> name <span class="token operator">=</span> person<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// TypeScript knows this is string</span> name<span class="token punctuation">.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ✓ Works</span> <span class="token keyword">let</span> age <span class="token operator">=</span> person<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// TypeScript knows this is number</span> age<span class="token punctuation">.</span><span class="token function">toFixed</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ✓ Works</span> <span class="token keyword">let</span> isActive <span class="token operator">=</span> person<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// TypeScript knows this is boolean</span> isActive<span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ✓ Works</span> |
Part 3: Optional Tuple Elements
Sometimes you want flexibility with count while maintaining type safety. TypeScript gives you optional elements:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<span class="token comment">// 3D coordinate with optional Z</span> <span class="token keyword">let</span> coordinate<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token operator">?</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">]</span><span class="token punctuation">;</span> coordinate <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">30</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Also fine</span> <span class="token comment">// HTTP status with optional message</span> <span class="token keyword">type</span> <span class="token class-name">HttpResponse</span> <span class="token operator">=</span> <span class="token punctuation">[</span>statusCode<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> statusText<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> body<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">let</span> response1<span class="token operator">:</span> HttpResponse <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">200</span><span class="token punctuation">,</span> <span class="token string">"OK"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">let</span> response2<span class="token operator">:</span> HttpResponse <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">404</span><span class="token punctuation">,</span> <span class="token string">"Not Found"</span><span class="token punctuation">,</span> <span class="token string">"<h1>404</h1>"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// TypeScript knows the optional one might be undefined</span> <span class="token keyword">let</span> body <span class="token operator">=</span> response2<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Type: string | undefined</span> |
Important: Optional elements must come AFTER required ones. This makes sense – you can’t skip the second element but include the third.
|
0 1 2 3 4 5 6 7 |
<span class="token comment">// This is NOT allowed:</span> <span class="token keyword">let</span> wrong<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">string</span><span class="token operator">?</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"hello"</span><span class="token punctuation">,</span> <span class="token number">42</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Error! Required parameter cannot follow optional parameter</span> |
Part 4: Rest Elements in Tuples
This is where tuples get really powerful. You can have a fixed beginning and then a variable-length end:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<span class="token comment">// A logging function that always has timestamp and level, then any number of messages</span> <span class="token keyword">type</span> <span class="token class-name">LogEntry</span> <span class="token operator">=</span> <span class="token punctuation">[</span>timestamp<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> level<span class="token operator">:</span> <span class="token string">"info"</span> <span class="token operator">|</span> <span class="token string">"warn"</span> <span class="token operator">|</span> <span class="token string">"error"</span><span class="token punctuation">,</span> <span class="token operator">...</span>messages<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">let</span> log1<span class="token operator">:</span> LogEntry <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1617293847</span><span class="token punctuation">,</span> <span class="token string">"info"</span><span class="token punctuation">,</span> <span class="token string">"User logged in"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">let</span> log2<span class="token operator">:</span> LogEntry <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1617293848</span><span class="token punctuation">,</span> <span class="token string">"error"</span><span class="token punctuation">,</span> <span class="token string">"Database connection failed"</span><span class="token punctuation">,</span> <span class="token string">"Retry 1"</span><span class="token punctuation">,</span> <span class="token string">"Retry 2"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">let</span> log3<span class="token operator">:</span> LogEntry <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1617293849</span><span class="token punctuation">,</span> <span class="token string">"warn"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Zero messages also works</span> <span class="token comment">// CSV data with fixed columns then variable additional data</span> <span class="token keyword">type</span> <span class="token class-name">CSVRow</span> <span class="token operator">=</span> <span class="token punctuation">[</span>id<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token operator">...</span>additionalFields<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">let</span> row1<span class="token operator">:</span> CSVRow <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token string">"Alice"</span><span class="token punctuation">,</span> <span class="token string">"alice@email.com"</span><span class="token punctuation">,</span> <span class="token string">"active"</span><span class="token punctuation">,</span> <span class="token string">"premium"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">let</span> row2<span class="token operator">:</span> CSVRow <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token string">"Bob"</span><span class="token punctuation">,</span> <span class="token string">"bob@email.com"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Just the required ones</span> |
Multiple Rest Elements? No.
|
0 1 2 3 4 5 6 7 |
<span class="token comment">// You can only have ONE rest element, and it must be last</span> <span class="token keyword">type</span> <span class="token class-name">Invalid</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span><span class="token builtin">number</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token operator">...</span><span class="token builtin">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Error! Rest element cannot follow another rest element</span> |
Part 5: Labeled Tuples (TypeScript 4.0+)
This is one of my favorite improvements. You can now label tuple elements for better documentation:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<span class="token comment">// Without labels - unclear what each position means</span> <span class="token keyword">type</span> <span class="token class-name">Person</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">string</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">let</span> alice<span class="token operator">:</span> Person <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"Alice"</span><span class="token punctuation">,</span> <span class="token number">30</span><span class="token punctuation">,</span> <span class="token string">"alice@email.com"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// With labels - crystal clear!</span> <span class="token keyword">type</span> <span class="token class-name">Person</span> <span class="token operator">=</span> <span class="token punctuation">[</span> name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> age<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> email<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">let</span> alice<span class="token operator">:</span> Person <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"Alice"</span><span class="token punctuation">,</span> <span class="token number">30</span><span class="token punctuation">,</span> <span class="token string">"alice@email.com"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// The labels don't change the behavior - they just improve readability</span> <span class="token keyword">let</span> <span class="token punctuation">[</span>personName<span class="token punctuation">,</span> personAge<span class="token punctuation">,</span> personEmail<span class="token punctuation">]</span> <span class="token operator">=</span> alice<span class="token punctuation">;</span> <span class="token comment">// You can still destructure with any names</span> |
Real-World Example: API Response
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<span class="token keyword">type</span> <span class="token class-name">ApiResult<span class="token operator"><</span><span class="token constant">T</span><span class="token operator">></span></span> <span class="token operator">=</span> <span class="token punctuation">[</span> data<span class="token operator">:</span> <span class="token constant">T</span> <span class="token operator">|</span> <span class="token keyword">null</span><span class="token punctuation">,</span> error<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token operator">|</span> <span class="token keyword">null</span><span class="token punctuation">,</span> status<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> timestamp<span class="token operator">:</span> Date <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">fetchUser</span><span class="token punctuation">(</span>id<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator"><</span>ApiResult<span class="token operator"><</span><span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token punctuation">}</span><span class="token operator">>></span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">/api/users/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>id<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token keyword">await</span> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">[</span>data<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> response<span class="token punctuation">.</span>status<span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token keyword">null</span><span class="token punctuation">,</span> err<span class="token punctuation">.</span>message<span class="token punctuation">,</span> <span class="token number">500</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Usage - the labels show up in IntelliSense!</span> <span class="token keyword">const</span> result <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetchUser</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// When you type "result." you see: data, error, status, timestamp</span> |
Part 6: Tuples vs Arrays – When to Use What
This is crucial. Let me give you a clear decision framework:
Use Tuples When:
-
The position implies meaning (coordinates, RGB values)
-
Fixed number of related values (key-value pairs, CSV rows)
-
Returning multiple values from functions (React’s useState)
-
Parameter lists that need type safety at each position
Use Arrays When:
-
Collection of same-type items (list of users, product IDs)
-
Variable length collections (shopping cart items)
-
You need array methods (map, filter, reduce)
-
Order doesn’t carry special meaning
Side-by-Side Comparison
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<span class="token comment">// ❌ Bad use of tuple - should be array</span> <span class="token keyword">let</span> productPrices<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">19.99</span><span class="token punctuation">,</span> <span class="token number">24.99</span><span class="token punctuation">,</span> <span class="token number">9.99</span><span class="token punctuation">,</span> <span class="token number">49.99</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// What if we add more products? We have to change the type every time!</span> <span class="token comment">// ✅ Good use of array</span> <span class="token keyword">let</span> productPrices<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">19.99</span><span class="token punctuation">,</span> <span class="token number">24.99</span><span class="token punctuation">,</span> <span class="token number">9.99</span><span class="token punctuation">,</span> <span class="token number">49.99</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// ✅ Good use of tuple - each position has specific meaning</span> <span class="token keyword">let</span> color<span class="token operator">:</span> <span class="token punctuation">[</span>red<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> green<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> blue<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> alpha<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">255</span><span class="token punctuation">,</span> <span class="token number">128</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Position 0 = red, position 1 = green, position 2 = blue, position 3 = opacity (optional)</span> |
Part 7: The Tuple Mutation Problem (Important!)
Here’s something that surprises many developers:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
<span class="token keyword">let</span> tuple<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"hello"</span><span class="token punctuation">,</span> <span class="token number">42</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// This works?! 😱</span> tuple<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// No error!</span> tuple<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">200</span><span class="token punctuation">;</span> <span class="token comment">// No error!</span> <span class="token comment">// Now tuple is ["hello", 42, 200] - we broke the tuple contract!</span> |
Why does this happen? Tuples in TypeScript are still arrays at runtime. The push, pop, splice, etc. methods are still there. TypeScript doesn’t prevent you from calling them.
The Solution: readonly Tuples
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<span class="token keyword">let</span> safeTuple<span class="token operator">:</span> <span class="token keyword">readonly</span> <span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"hello"</span><span class="token punctuation">,</span> <span class="token number">42</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Now these all error:</span> safeTuple<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Error! Property 'push' does not exist on type 'readonly [string, number]'</span> safeTuple<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">200</span><span class="token punctuation">;</span> <span class="token comment">// Error! Index signature in readonly tuple only permits reading</span> safeTuple<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">"world"</span><span class="token punctuation">;</span> <span class="token comment">// Error! Cannot assign to '0' because it is a read-only property</span> <span class="token comment">// You can still read normally:</span> <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>safeTuple<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ✓ Works</span> |
Pro tip: Make your tuples readonly by default. You can always remove readonly if you need mutation, but you can’t add it back after the fact!
Part 8: Destructuring Tuples
Tuples and destructuring are best friends:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<span class="token comment">// Basic destructuring</span> <span class="token keyword">let</span> user<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">boolean</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"Alice"</span><span class="token punctuation">,</span> <span class="token number">30</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">let</span> <span class="token punctuation">[</span>name<span class="token punctuation">,</span> age<span class="token punctuation">,</span> isActive<span class="token punctuation">]</span> <span class="token operator">=</span> user<span class="token punctuation">;</span> <span class="token comment">// name: string, age: number, isActive: boolean</span> <span class="token comment">// Skipping elements</span> <span class="token keyword">let</span> <span class="token punctuation">[</span><span class="token punctuation">,</span> <span class="token punctuation">,</span> active<span class="token punctuation">]</span> <span class="token operator">=</span> user<span class="token punctuation">;</span> <span class="token comment">// Just get the third element</span> <span class="token comment">// Rest with tuples</span> <span class="token keyword">let</span> rgb<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">255</span><span class="token punctuation">,</span> <span class="token number">128</span><span class="token punctuation">,</span> <span class="token number">64</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">let</span> <span class="token punctuation">[</span>red<span class="token punctuation">,</span> <span class="token operator">...</span>rest<span class="token punctuation">]</span> <span class="token operator">=</span> rgb<span class="token punctuation">;</span> <span class="token comment">// rest is [128, 64] - TypeScript infers: [number, number]</span> <span class="token comment">// Destructuring in function parameters</span> <span class="token keyword">function</span> <span class="token function">printCoordinate</span><span class="token punctuation">(</span><span class="token punctuation">[</span>x<span class="token punctuation">,</span> y<span class="token punctuation">]</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span> <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">X: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>x<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">, Y: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>y<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">printCoordinate</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ✓ Works</span> <span class="token function">printCoordinate</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">30</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Error! Too many elements</span> |
Real-World: React’s useState
|
0 1 2 3 4 5 6 7 8 9 10 11 |
<span class="token comment">// This is actually a tuple!</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>count<span class="token punctuation">,</span> setCount<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token generic-function"><span class="token function">useState</span><span class="token generic class-name"><span class="token operator"><</span><span class="token builtin">number</span><span class="token operator">></span></span></span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Type: [number, Dispatch<SetStateAction<number>>]</span> <span class="token comment">// You could write it explicitly:</span> <span class="token keyword">const</span> state<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>newCount<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">void</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Part 9: Advanced Tuple Patterns
Generic Tuples
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<span class="token comment">// A function that returns a pair</span> <span class="token keyword">function</span> <span class="token generic-function"><span class="token function">pair</span><span class="token generic class-name"><span class="token operator"><</span><span class="token constant">T</span><span class="token punctuation">,</span> <span class="token constant">U</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>first<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">,</span> second<span class="token operator">:</span> <span class="token constant">U</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token constant">T</span><span class="token punctuation">,</span> <span class="token constant">U</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">[</span>first<span class="token punctuation">,</span> second<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> numberStringPair <span class="token operator">=</span> <span class="token function">pair</span><span class="token punctuation">(</span><span class="token number">42</span><span class="token punctuation">,</span> <span class="token string">"answer"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Type: [number, string]</span> <span class="token keyword">const</span> booleanArrayPair <span class="token operator">=</span> <span class="token function">pair</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Type: [boolean, number[]]</span> <span class="token comment">// A function that swaps tuple elements</span> <span class="token keyword">function</span> <span class="token generic-function"><span class="token function">swap</span><span class="token generic class-name"><span class="token operator"><</span><span class="token constant">T</span><span class="token punctuation">,</span> <span class="token constant">U</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>tuple<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token constant">T</span><span class="token punctuation">,</span> <span class="token constant">U</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token constant">U</span><span class="token punctuation">,</span> <span class="token constant">T</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">[</span>tuple<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span> tuple<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> swapped <span class="token operator">=</span> <span class="token function">swap</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"hello"</span><span class="token punctuation">,</span> <span class="token number">42</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Type: [number, string]</span> |
Tuple Union Types
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<span class="token keyword">type</span> <span class="token class-name">Event</span> <span class="token operator">=</span> <span class="token operator">|</span> <span class="token punctuation">[</span><span class="token string">"click"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> x<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> y<span class="token operator">:</span> <span class="token builtin">number</span> <span class="token punctuation">}</span><span class="token punctuation">]</span> <span class="token operator">|</span> <span class="token punctuation">[</span><span class="token string">"keypress"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> key<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token punctuation">}</span><span class="token punctuation">]</span> <span class="token operator">|</span> <span class="token punctuation">[</span><span class="token string">"focus"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> elementId<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">handleEvent</span><span class="token punctuation">(</span>event<span class="token operator">:</span> Event<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// TypeScript narrows the tuple based on the first element (discriminated union)</span> <span class="token keyword">switch</span> <span class="token punctuation">(</span>event<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> <span class="token string">"click"</span><span class="token operator">:</span> <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Clicked at </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>event<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>x<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">, </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>event<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>y<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ✓ TypeScript knows</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> <span class="token string">"keypress"</span><span class="token operator">:</span> <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Key pressed: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>event<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>key<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ✓ TypeScript knows</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> <span class="token string">"focus"</span><span class="token operator">:</span> <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Focused on: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>event<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>elementId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ✓ TypeScript knows</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Inferring Tuple Types
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<span class="token comment">// TypeScript can infer tuples with const assertions</span> <span class="token keyword">const</span> route <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"users"</span><span class="token punctuation">,</span> <span class="token number">123</span><span class="token punctuation">,</span> <span class="token string">"profile"</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">const</span><span class="token punctuation">;</span> <span class="token comment">// Type: readonly ["users", 123, "profile"] - a specific tuple!</span> <span class="token keyword">type</span> <span class="token class-name">Route</span> <span class="token operator">=</span> <span class="token keyword">typeof</span> route<span class="token punctuation">;</span> <span class="token comment">// readonly ["users", 123, "profile"]</span> <span class="token keyword">type</span> <span class="token class-name">Path</span> <span class="token operator">=</span> Route<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// "users"</span> <span class="token keyword">type</span> <span class="token class-name">Id</span> <span class="token operator">=</span> Route<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// 123</span> <span class="token keyword">type</span> <span class="token class-name">Section</span> <span class="token operator">=</span> Route<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// "profile"</span> <span class="token comment">// This is incredibly useful for creating type-safe lookup tables</span> <span class="token keyword">const</span> colorCodes <span class="token operator">=</span> <span class="token punctuation">{</span> red<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">255</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">,</span> green<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">255</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">,</span> blue<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">255</span><span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token keyword">as</span> <span class="token keyword">const</span><span class="token punctuation">;</span> <span class="token keyword">type</span> <span class="token class-name">ColorName</span> <span class="token operator">=</span> <span class="token keyword">keyof</span> <span class="token keyword">typeof</span> colorCodes<span class="token punctuation">;</span> <span class="token comment">// "red" | "green" | "blue"</span> <span class="token keyword">type</span> <span class="token class-name"><span class="token constant">RGB</span></span> <span class="token operator">=</span> <span class="token keyword">typeof</span> colorCodes<span class="token punctuation">[</span>ColorName<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// readonly [255, 0, 0] | readonly [0, 255, 0] | readonly [0, 0, 255]</span> |
Part 10: Real-World Use Cases
Case Study 1: Database Query Results
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<span class="token comment">// Simulating a database row</span> <span class="token keyword">type</span> <span class="token class-name">UserRow</span> <span class="token operator">=</span> <span class="token punctuation">[</span> id<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> username<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> email<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> createdAt<span class="token operator">:</span> Date<span class="token punctuation">,</span> isActive<span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">Database</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> users<span class="token operator">:</span> UserRow<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token string">"alice"</span><span class="token punctuation">,</span> <span class="token string">"alice@email.com"</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token string">"2023-01-01"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token string">"bob"</span><span class="token punctuation">,</span> <span class="token string">"bob@email.com"</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token string">"2023-01-02"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">]</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token function">findById</span><span class="token punctuation">(</span>id<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span><span class="token operator">:</span> UserRow <span class="token operator">|</span> <span class="token keyword">undefined</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>users<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span>row <span class="token operator">=></span> row<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">===</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">create</span><span class="token punctuation">(</span>username<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> email<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> UserRow <span class="token punctuation">{</span> <span class="token keyword">const</span> newRow<span class="token operator">:</span> UserRow <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token keyword">this</span><span class="token punctuation">.</span>users<span class="token punctuation">.</span>length <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span> username<span class="token punctuation">,</span> email<span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token boolean">true</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>users<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>newRow<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> newRow<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> db <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Database</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> user <span class="token operator">=</span> db<span class="token punctuation">.</span><span class="token function">findById</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>user<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>id<span class="token punctuation">,</span> username<span class="token punctuation">,</span> email<span class="token punctuation">,</span> createdAt<span class="token punctuation">,</span> isActive<span class="token punctuation">]</span> <span class="token operator">=</span> user<span class="token punctuation">;</span> <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>username<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> (</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>email<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">) - Active: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>isActive<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Case Study 2: State Machine Transitions
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<span class="token keyword">type</span> <span class="token class-name">State</span> <span class="token operator">=</span> <span class="token string">"idle"</span> <span class="token operator">|</span> <span class="token string">"loading"</span> <span class="token operator">|</span> <span class="token string">"success"</span> <span class="token operator">|</span> <span class="token string">"error"</span><span class="token punctuation">;</span> <span class="token keyword">type</span> <span class="token class-name">Transition</span> <span class="token operator">=</span> <span class="token punctuation">[</span> from<span class="token operator">:</span> State<span class="token punctuation">,</span> to<span class="token operator">:</span> State<span class="token punctuation">,</span> action<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">const</span> allowedTransitions<span class="token operator">:</span> <span class="token keyword">readonly</span> Transition<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">[</span><span class="token string">"idle"</span><span class="token punctuation">,</span> <span class="token string">"loading"</span><span class="token punctuation">,</span> <span class="token string">"FETCH_START"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">"loading"</span><span class="token punctuation">,</span> <span class="token string">"success"</span><span class="token punctuation">,</span> <span class="token string">"FETCH_SUCCESS"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">"loading"</span><span class="token punctuation">,</span> <span class="token string">"error"</span><span class="token punctuation">,</span> <span class="token string">"FETCH_ERROR"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">"success"</span><span class="token punctuation">,</span> <span class="token string">"idle"</span><span class="token punctuation">,</span> <span class="token string">"RESET"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">"error"</span><span class="token punctuation">,</span> <span class="token string">"idle"</span><span class="token punctuation">,</span> <span class="token string">"RESET"</span><span class="token punctuation">]</span> <span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">const</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">StateMachine</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> currentState<span class="token operator">:</span> State <span class="token operator">=</span> <span class="token string">"idle"</span><span class="token punctuation">;</span> <span class="token function">transition</span><span class="token punctuation">(</span>action<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> transition <span class="token operator">=</span> allowedTransitions<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span> <span class="token punctuation">(</span><span class="token punctuation">[</span>from<span class="token punctuation">,</span> to<span class="token punctuation">,</span> act<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">=></span> from <span class="token operator">===</span> <span class="token keyword">this</span><span class="token punctuation">.</span>currentState <span class="token operator">&&</span> act <span class="token operator">===</span> action <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>transition<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>_<span class="token punctuation">,</span> newState<span class="token punctuation">]</span> <span class="token operator">=</span> transition<span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>currentState <span class="token operator">=</span> newState<span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Case Study 3: CSV Parser
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<span class="token keyword">type</span> <span class="token class-name">CSVRow</span> <span class="token operator">=</span> <span class="token punctuation">[</span>id<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> email<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> score<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">CSVParser</span> <span class="token punctuation">{</span> <span class="token function">parseRow</span><span class="token punctuation">(</span>row<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> CSVRow <span class="token operator">|</span> <span class="token keyword">null</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> columns <span class="token operator">=</span> row<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">','</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>columns<span class="token punctuation">.</span>length <span class="token operator">!==</span> <span class="token number">4</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>id<span class="token punctuation">,</span> name<span class="token punctuation">,</span> email<span class="token punctuation">,</span> score<span class="token punctuation">]</span> <span class="token operator">=</span> columns<span class="token punctuation">;</span> <span class="token keyword">const</span> parsedId <span class="token operator">=</span> <span class="token function">parseInt</span><span class="token punctuation">(</span>id<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> parsedScore <span class="token operator">=</span> <span class="token function">parseFloat</span><span class="token punctuation">(</span>score<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isNaN</span><span class="token punctuation">(</span>parsedId<span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token function">isNaN</span><span class="token punctuation">(</span>parsedScore<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token punctuation">[</span>parsedId<span class="token punctuation">,</span> name<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> email<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> parsedScore<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">parse</span><span class="token punctuation">(</span>data<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> CSVRow<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> data <span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">'\n'</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>row <span class="token operator">=></span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">parseRow</span><span class="token punctuation">(</span>row<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span>row<span class="token punctuation">)</span><span class="token operator">:</span> row <span class="token keyword">is</span> CSVRow <span class="token operator">=></span> row <span class="token operator">!==</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> csv <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CSVParser</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> rows <span class="token operator">=</span> csv<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span></span> 1,Alice,alice@example.com,95.5 2,Bob,bob@example.com,87.0 3,Charlie,charlie@example.com,92.5 <span class="token template-punctuation string">`</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Part 11: Common Mistakes and How to Avoid Them
Mistake 1: Assuming Tuple Length Is Enforced
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
<span class="token keyword">let</span> tuple<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"hello"</span><span class="token punctuation">,</span> <span class="token number">42</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// This is allowed at compile time!</span> tuple <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"world"</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">,</span> <span class="token string">"extra"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// No error in some TypeScript versions</span> <span class="token comment">// Solution: Use readonly</span> <span class="token keyword">let</span> safeTuple<span class="token operator">:</span> <span class="token keyword">readonly</span> <span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"hello"</span><span class="token punctuation">,</span> <span class="token number">42</span><span class="token punctuation">]</span><span class="token punctuation">;</span> safeTuple <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"world"</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">,</span> <span class="token string">"extra"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Error! Source has 3 elements, target expects 2</span> |
Mistake 2: Forgetting That .length Is Literal
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<span class="token keyword">let</span> tuple<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"hello"</span><span class="token punctuation">,</span> <span class="token number">42</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">let</span> length <span class="token operator">=</span> tuple<span class="token punctuation">.</span>length<span class="token punctuation">;</span> <span class="token comment">// TypeScript infers: 2 (literal type, not number!)</span> <span class="token comment">// This can cause issues:</span> <span class="token keyword">function</span> <span class="token function">processLength</span><span class="token punctuation">(</span>len<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Processing exactly 2 elements"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">processLength</span><span class="token punctuation">(</span>tuple<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ✓ Works! TypeScript knows it's exactly 2</span> <span class="token keyword">let</span> array <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"hello"</span><span class="token punctuation">,</span> <span class="token number">42</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// TypeScript infers: (string | number)[]</span> <span class="token function">processLength</span><span class="token punctuation">(</span>array<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Error! Type 'number' is not assignable to type '2'</span> |
Mistake 3: Confusing Tuples with Arrays in Function Returns
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<span class="token comment">// ❌ This returns an array, not a tuple</span> <span class="token keyword">function</span> <span class="token function">getCoordinates</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token builtin">number</span> <span class="token operator">|</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Type is (number | null)[] </span> <span class="token punctuation">}</span> <span class="token comment">// ✅ This returns a tuple</span> <span class="token keyword">function</span> <span class="token function">getCoordinates</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">number</span> <span class="token operator">|</span> <span class="token keyword">null</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Type is [number, number | null]</span> <span class="token punctuation">}</span> |
Part 12: Performance and Best Practices
Best Practice 1: Use as const for Literal Tuples
|
0 1 2 3 4 5 6 7 8 9 10 11 |
<span class="token comment">// Instead of:</span> <span class="token keyword">const</span> position<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">number</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Consider:</span> <span class="token keyword">const</span> position <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">const</span><span class="token punctuation">;</span> <span class="token comment">// Type: readonly [10, 20] - even more specific!</span> |
Best Practice 2: Prefer Interfaces for Complex Data
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<span class="token comment">// If you have more than 3-4 elements, consider if you need an interface instead</span> <span class="token keyword">type</span> <span class="token class-name">Person</span> <span class="token operator">=</span> <span class="token punctuation">[</span>name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> age<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> email<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> phone<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> address<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// This is getting hard to remember - which position is phone? which is email?</span> <span class="token comment">// Better:</span> <span class="token keyword">interface</span> <span class="token class-name">Person</span> <span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> age<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> email<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> phone<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> address<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Best Practice 3: Document Your Tuples
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<span class="token comment">// Good - self-documenting with labels</span> <span class="token keyword">type</span> <span class="token class-name"><span class="token constant">RGB</span></span> <span class="token operator">=</span> <span class="token punctuation">[</span> red<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token comment">// 0-255</span> green<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token comment">// 0-255</span> blue<span class="token operator">:</span> <span class="token builtin">number</span> <span class="token comment">// 0-255</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Even better - add a comment</span> <span class="token comment">/**</span> * Represents an RGB color * @example [255, 0, 0] for red */ <span class="token keyword">type</span> <span class="token class-name"><span class="token constant">RGB</span></span> <span class="token operator">=</span> <span class="token punctuation">[</span>red<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> green<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> blue<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">]</span><span class="token punctuation">;</span> |
Summary: The Tuple Philosophy
Tuples in TypeScript represent a beautiful compromise between JavaScript’s flexible arrays and the need for precise typing. They say:
-
“I know exactly what goes where” – Position has meaning
-
“I know exactly how many” – Length is fixed
-
“Each slot has its own type” – Not just a union
When you use tuples correctly, your code becomes self-documenting. When you see [string, number], you immediately know it’s a pair with specific meanings at each position. When you see string[], you know it’s a collection of the same thing.
Remember: Tuples are for fixed patterns, arrays are for collections. Choose the right tool for the job, and always consider making your tuples readonly to prevent accidental mutations.
Does this help clarify TypeScript tuples? Would you like me to elaborate on any specific aspect or show more real-world examples?
