Annotated solution
Published September 13, 2026The solution
type StrLength<S extends string, R extends unknown[] = []> = S extends `${string}${infer Rest}` ? StrLength<Rest, [...R, unknown]> : R['length']
The common wrong answer
type StrLength<S extends string, R extends unknown[] = []> = S['length']A string type does have a length, and it is number — the general one, not 5. Only tuples carry their length as a literal type. This is the reason nearly every counting type in TypeScript is written with a tuple accumulator that nobody ever reads the contents of.
Line by line
`${string}${infer Rest}`The first placeholder is
stringrather thaninferbecause the character itself does not matter — only that there was one.[...R, unknown]The accumulator is a tally. Its element type is irrelevant;
unknownis chosen precisely because nothing will ever look inside it.R['length']And here the tally is cashed in. Because
Ris a tuple, itslengthis the literal5, which is what the check is asking for.
Takeaway
Numbers at the type level are tuple lengths. Any time you need to count something, the answer is a tuple you grow and then ask for its length.

