35191 · Trace
35191 · Trace
Problem
Implement Trace<T> for a square matrix (2D tuple) that returns the union of all diagonal elements — i.e., T[0][0] | T[1][1] | T[2][2] | ...
type cases = [
Expect<Equal<Trace<[[1, 2], [3, 4]]>, 1 | 4>>,
Expect<Equal<Trace<[[0, 1, 1], [2, 0, 2], [3, 3, 0]]>, 0>>,
Expect<Equal<Trace<[['a', 'b', ''], ['c', '', ''], ['d', 'e', 'f']]>, 'a' | '' | 'f'>>,
]Solution
type Trace<T extends any[][], Idx extends any[] = []> =
Idx['length'] extends T['length']
? never
: T[Idx['length']][Idx['length']] | Trace<T, [...Idx, any]>Explanation
The solution uses a counter tuple Idx to track the current index and collect diagonal elements.
Step by step:
Idx extends any[] = []— starts as an empty tuple (length 0), used as a type-level counter.Idx['length'] extends T['length']— base case: when the counter reaches the number of rows, stop and returnnever(no more diagonal elements).T[Idx['length']][Idx['length']]— accesses the element at position[i][i]wherei = Idx['length'].Idx['length']is a numeric literal type (0, 1, 2, ...) that can index into tuple types.
Trace<T, [...Idx, any]>— recurse withIdxgrown by one element, incrementing the counter.The union
|accumulates all diagonal values across iterations.
Why use a tuple as counter?
TypeScript doesn't support arithmetic on numeric literal types directly. The standard trick is to use a tuple whose length property gives the current count. Adding any to the tuple increments the counter by 1.
Example: For [[1,2],[3,4]]:
Idx=[](length 0):T[0][0] = 1, recurseIdx=[any](length 1):T[1][1] = 4, recurseIdx=[any,any](length 2): equalsT['length'](2), returnnever- Result:
1 | 4 | never = 1 | 4
Key Concepts
- Tuple-as-counter pattern —
[...Idx, any]increments a type-level counter - Numeric indexing of tuples —
T[N]whereNis a literal number type accesses the Nth element - Recursive union building —
A | Recurse<...>accumulates a union type across iterations
