Skip to content
webtype.org#225 trim-string · par 3

No. 225 · August 2, 2026 · Moderate

Trim

Implement `Trim<S>` so it removes whitespace from both ends of a string type. `Whitespace` is in scope and covers spaces, tabs and newlines.

01

Try the puzzle yourself

Par 3

Puzzle

trim-string.ts
type Whitespace = ' ' | '\n' | '\t'
Stroke 1 of 3Not run yet

Replace ??? — your solution is checked against the cases below. Tab indents; press Escape then Tab to move focus out.

Checks

4
  • Trim<' hello '>
    'hello'
  • Trim<'nospace'>
    'nospace'
  • Trim<'\ttabbed\t'>
    'tabbed'
  • Trim<' '>
    ''

How a check is judged Exact type equality, not assignability — an intersection is not the same as the flattened object.

How everyone did

Fewer than 5 people have solved this one so far. The distribution appears once there is enough of a sample to mean anything.

Archive
02

Annotated solution

Published August 3, 2026

The solution

type Trim<S extends string> = S extends `${Whitespace}${infer R}`
  ? Trim<R>
  : S extends `${infer R}${Whitespace}`
    ? Trim<R>
    : S

The common wrong answer

type Trim<S extends string> = S extends `${Whitespace}${infer R}${Whitespace}`
  ? Trim<R>
  : S

Demanding whitespace at both ends in one pattern fails as soon as only one end has any: `" left"` does not match, so nothing is trimmed. The two ends have to be handled independently.

Line by line

  1. `${Whitespace}${infer R}`

    `Whitespace` is a union, so this pattern is really three patterns tried together — a template literal matches if any member of the union fits the slot.

  2. Trim<R>

    One character comes off per pass, and the result is fed back in. Both branches recurse into the same type, so leading and trailing whitespace are consumed by the same loop.

Takeaway

A union inside a template literal pattern multiplies the patterns tried. Ordered conditional branches then let you strip one end at a time without writing two separate types.