First is a small Type Challenges problem, but it teaches a very reusable TypeScript idea: you can pattern-match a tuple type and infer just the part you need.
If you have already worked through how to implement Pick in TypeScript, how to implement Readonly in TypeScript, and how to implement Tuple to Object in TypeScript, this is a nice next step. Those problems focus on keys, mapped types, or tuple values becoming unions. First is about destructuring a tuple shape directly in the type system.
In this article, we will answer four practical questions:
- What exactly is the challenge checking?
- Why should
Tbe constrained toreadonly unknown[]? - How does
readonly [infer F, ...infer _]extract the first element type? - Why does an empty tuple return
never?
1. Start with what the challenge is really asking for
The original example is short:
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type head1 = First<arr1> // 'a'
type head2 = First<arr2> // 3
That already tells us the whole goal.
This challenge is not about reading a runtime array value. It is about taking a tuple or array type and returning the type of its first position.
So:
First<['a', 'b', 'c']>should be'a'First<[3, 2, 1]>should be3
The tests make the behavior more precise:
type cases = [
First<[3, 2, 1]>,
First<[() => 123, { a: string }]>,
First<[]>,
First<[undefined]>,
]
type errors = [
// @ts-expect-error
First<'notArray'>,
// @ts-expect-error
First<{ 0: 'arrayLike' }>,
]
Those cases are checking four things:
- a normal tuple should return the first literal type
- the first item can be any full type, including a function type
- an empty tuple should return
never - non-array inputs should be rejected by the generic constraint
That last point matters because the solution is not only about extracting a type. It is also about preventing invalid input.
2. The complete solution is short
Here is a clean implementation:
type First<T extends readonly unknown[]> =
T extends readonly [infer F, ...infer _] ? F : never
There are two important parts:
T extends readonly unknown[]T extends readonly [infer F, ...infer _] ? F : never
The first part narrows what counts as a valid input. The second part checks whether the tuple is non-empty and, if so, infers the first element type.
3. Why the generic constraint matters
Start with this part:
T extends readonly unknown[]
It means T must be some array or tuple type.
That is why these should fail:
First<'notArray'>
First<{ 0: 'arrayLike' }>
Neither of those inputs is actually an array or tuple, so the constraint blocks them before the conditional logic even runs.
Using readonly unknown[] is slightly more flexible than unknown[], because it also accepts readonly tuples created by as const.
For example:
const values = [1, 2, 3] as const
The type of values is:
readonly [1, 2, 3]
If your helper only accepted mutable arrays, that common tuple shape would no longer fit cleanly.
4. How infer extracts the first element type
Now look at the core pattern:
T extends readonly [infer F, ...infer _] ? F : never
Read it like this:
- if
Tcan be matched as a tuple with at least one element - infer the first element type as
F - ignore the rest of the tuple
- return
F
This is type-level tuple destructuring.
Take this example:
type A = First<[3, 2, 1]>
The tuple [3, 2, 1] matches:
readonly [infer F, ...infer _]
so F becomes 3, and the final result is:
type A = 3
The same logic works when the first element is a function type:
type B = First<[() => 123, { a: string }]>
Here the first position is () => 123, so F is inferred as that exact function type:
type B = () => 123
That is an important detail.
The helper does not transform the first item.
It simply returns the full type found at index 0.
5. Why the empty tuple case becomes never
Now consider the empty tuple:
type C = First<[]>
An empty tuple cannot match this pattern:
readonly [infer F, ...infer _]
because that pattern requires at least one element.
So the condition fails, and TypeScript takes the false branch:
type C = never
That matches the challenge exactly.
never is the right result here because there is no first element type to return.
This is different from undefined.
undefined would mean "there is a value here and its type is undefined."
But never means "there is no valid value to extract at all."
6. Why readonly appears in both places
You will often see a shorter version like this:
type First<T extends readonly unknown[]> =
T extends [infer F, ...infer _] ? F : never
That works for plain tuples in the challenge tests, but the fully readonly version is more consistent:
type First<T extends readonly unknown[]> =
T extends readonly [infer F, ...infer _] ? F : never
The reason is simple. If the input is a readonly tuple, the pattern should also be written as readonly.
That keeps the helper aligned with common as const inputs and avoids mixing a readonly constraint with a mutable tuple pattern.
7. The T[0] alternative is also worth knowing
You will also see an indexed-access version:
type First<T extends readonly unknown[]> =
T extends readonly [] ? never : T[0]
This approach is valid too. It handles the empty tuple case explicitly, then uses indexed access to read the first element type.
Compared with the infer version, the tradeoff is mostly about teaching style:
- the
inferversion shows how tuple pattern matching works - the
T[0]version shows how indexed access works on array-like types
For this particular challenge, the infer version is often easier to read because the "first element plus the rest" structure is visible in one place.
8. Two beginner mistakes are especially common
Forgetting the array constraint
If T is unconstrained, invalid inputs such as strings or array-like objects are no longer rejected at the type boundary.
Treating never like undefined
For First<[]>, the result should be never, not undefined.
The point is not that the first element exists and happens to be undefined.
The point is that there is no first element type to extract.
9. One line of code, one reusable pattern
The final answer is still:
type First<T extends readonly unknown[]> =
T extends readonly [infer F, ...infer _] ? F : never
But behind that one line are three TypeScript ideas worth keeping:
- constrain the generic so only real array or tuple inputs are accepted
- use tuple pattern matching to check whether the input is non-empty
- infer the first piece you care about and ignore the rest
Once that mental model feels natural, a lot of later Type Challenges problems start looking much less mysterious.