Yuzhe's Blog

yuzhes

35991 · MyUppercase

35991 · MyUppercase

Challenge Link

Problem

Implement MyUppercase<T> that converts every lowercase letter in string T to its uppercase equivalent, without using the built-in Uppercase<T>.

type cases = [
  Expect<Equal<MyUppercase<'a'>, 'A'>>,
  Expect<Equal<MyUppercase<'Z'>, 'Z'>>,
  Expect<Equal<MyUppercase<'A z h yy 😃cda\n\t  a   '>, 'A Z H YY 😃CDA\n\t  A   '>>,
]

Solution

type UpperMap = {
  'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D', 'e': 'E', 'f': 'F', 'g': 'G', 'h': 'H',
  'i': 'I', 'j': 'J', 'k': 'K', 'l': 'L', 'm': 'M', 'n': 'N', 'o': 'O', 'p': 'P',
  'q': 'Q', 'r': 'R', 's': 'S', 't': 'T', 'u': 'U', 'v': 'V', 'w': 'W', 'x': 'X',
  'y': 'Y', 'z': 'Z'
}

type MyUppercase<T extends string> =
  T extends `${infer C}${infer Rest}`
    ? C extends keyof UpperMap
      ? `${UpperMap[C]}${MyUppercase<Rest>}`
      : `${C}${MyUppercase<Rest>}`
    : T

Explanation

The solution uses a lookup table (UpperMap) and template literal recursion to process the string character by character.

The Lookup Table

UpperMap is a plain object type mapping each lowercase letter to its uppercase counterpart. This avoids any built-in utilities and makes the mapping explicit and inspectable.

The Recursion

MyUppercase<T> splits T into the first character C and the remaining string Rest using template literal infer:

T extends `${infer C}${infer Rest}`

TypeScript greedily matches: C gets the first character, Rest gets everything after.

Why not use Uppercase<C>?

The challenge asks us to implement it manually. The UpperMap approach is conceptually equivalent — a finite lookup table covers all 26 lowercase letters.

Performance note: Template literal recursion in TypeScript can hit depth limits for very long strings. For typical usage this is fine; TypeScript limits recursive types to ~100 levels by default.

Key Concepts