---
title: TypeScript Challenges
description: Hands-on type challenges to practice and deepen TypeScript type-level programming skills.
tokens: ~3039
---

# TypeScript Challenges

> **Work in Progress** — A collection of solutions to TypeScript type challenges sourced from [BFE.dev TypeScript](https://bigfrontend.dev/typescript), [type-challenges](https://github.com/type-challenges/type-challenges), and [TypeScript Cookbook](https://typescript-cookbook.com/recipes/).

## Key Techniques

| Technique                             | Use case                                                                                        |
| ------------------------------------- | ----------------------------------------------------------------------------------------------- |
| Mapped types <br/> `[K in keyof T]`   | Transform every property of an object type (add/remove modifiers, remap values)                 |
| `keyof` / `T[number]` indexed access  | Extract keys from objects or element types from tuples as unions                                |
| Conditional types <br/> `extends ? :` | Branch logic at the type level — pattern match and narrow types                                 |
| Distributive conditionals             | Filter or transform each member of a union independently                                        |
| `infer` keyword                       | Capture an unknown type from inside a structure (tuple element, promise inner, function params) |
| Recursive types                       | Peel through nested or variable-length structures one layer at a time                           |
| Variadic tuple types <br/> `...T`     | Spread, concatenate, or destructure tuple types                                                 |

---

## Challenges

### Pick

Re-implement the built-in `Pick<T, K>`. Constrain `K` to valid keys of `T`, then use a mapped type to iterate over those keys.

```typescript
type MyPick<T, K extends keyof T> = {
  [P in K]: T[P];
};
```

**Concepts:** mapped types, `keyof`, generic constraints.

### Readonly

Re-implement the built-in `Readonly<T>`. Add the `readonly` modifier to every property via a mapped type.

```typescript
type MyReadonly<T> = {
  readonly [P in keyof T]: T[P];
};
```

**Concepts:** mapped types, `readonly` modifier.

### Tuple to Object

Transform a `readonly` tuple into an object where each element becomes both key and value. `T[number]` produces a union of all tuple element types.

```typescript
type TupleToObject<T extends readonly (string | number | symbol)[]> = {
  [P in T[number]]: P;
};
```

**Concepts:** indexed access with `T[number]`, mapped types, `PropertyKey` constraint.

### First of Array

Return the first element's type, or `never` for empty arrays. Check length to handle the empty case.

```typescript
type First<T extends any[]> = T["length"] extends 0 ? never : T[0];
```

Alternative using `infer`:

```typescript
type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;
```

**Concepts:** conditional types, tuple length check, `infer` with rest elements.

### Length of Tuple

Access the `length` property of a tuple type to get a numeric literal. The `readonly` constraint ensures `as const` arrays are accepted.

```typescript
type Length<T extends readonly unknown[]> = T["length"];
```

**Concepts:** tuple `length` property returns a numeric literal (not `number`).

### Exclude ⚡

Re-implement `Exclude<T, U>`. Conditional types distribute over unions automatically — each member of `T` is tested against `U` independently.

```typescript
type MyExclude<T, U> = T extends U ? never : T;
```

**Concepts:** distributive conditional types, union filtering.

### Awaited

Recursively unwrap `Promise` (or any thenable) to get the final resolved type. Uses `PromiseLike` instead of `Promise` to handle custom thenables.

```typescript
type MyAwaited<T> = T extends PromiseLike<infer Inner> ? MyAwaited<Inner> : T;
```

**Concepts:** recursive conditional types, `infer`, `PromiseLike` for thenable compatibility.

### If

Type-level ternary. Constrain `C` to `boolean`, then branch on `true`.

```typescript
type If<C extends boolean, T, F> = C extends true ? T : F;
```

When `C` is `boolean` (i.e. `true | false`), the conditional distributes and returns `T | F`.

**Concepts:** conditional types, `boolean` = `true | false` distribution.

### Concat

Use variadic tuple types to spread both arrays into a new one.

```typescript
type Concat<T extends readonly unknown[], U extends readonly unknown[]> = [
  ...T,
  ...U,
];
```

**Concepts:** variadic tuple types, spread in tuple types.

### Includes ⚡

Check whether a tuple contains an element with exact equality. Requires recursive iteration — simple `extends` checks fail on edge cases like `boolean` vs `true`, `{ a: 'A' }` vs `{ readonly a: 'A' }`, and union types.

```typescript
import type { Equal } from "@type-challenges/utils";

type Includes<T extends readonly any[], U> = T extends [
  infer First,
  ...infer Rest,
]
  ? Equal<First, U> extends true
    ? true
    : Includes<Rest, U>
  : false;
```

`Equal` performs a strict structural check that `extends` alone cannot. The recursion peels off one element at a time until a match is found or the tuple is exhausted.

**Concepts:** recursive conditional types, `infer` with rest, exact type equality vs assignability.

### Push

Append an element to a tuple using spread.

```typescript
type Push<T extends unknown[], U> = [...T, U];
```

**Concepts:** variadic tuple types.

### Unshift

Prepend an element to a tuple using spread.

```typescript
type Unshift<T extends unknown[], U> = [U, ...T];
```

**Concepts:** variadic tuple types.

### Parameters

Re-implement `Parameters<T>`. Use `infer` in the function argument position to capture the parameter tuple.

```typescript
type MyParameters<T extends (...args: any[]) => any> = T extends (
  ...args: infer P
) => any
  ? P
  : never;
```

**Concepts:** `infer` in function types, function type constraints.

### Append to Object

Add a new key–value pair to an existing object type. Union `keyof T` with the new key `U` in the mapped type, then conditionally resolve the value type.

```typescript
type AppendToObject<T, U extends PropertyKey, V> = {
  [K in keyof T | U]: K extends keyof T ? T[K] : V;
};
```

The `keyof T | U` union ensures existing keys keep their original types while `U` gets value type `V`.

**Concepts:** mapped types, union in `in` clause, conditional value resolution.

### Merge

Merge two object types into one, with the second type's properties overriding the first's when keys collide.

```typescript
type Merge<F, S> = {
  [K in keyof F | keyof S]: K extends keyof S
    ? S[K]
    : K extends keyof F
      ? F[K]
      : never;
};
```

The priority chain `S → F → never` resolves each key: `S` wins on overlap, `F` fills the rest, and `never` is unreachable since `K` is constrained to `keyof F | keyof S`.

**Concepts:** mapped types, union of `keyof`, nested conditional types for priority merge.

### IsNever

Check whether a type resolves to `never`. Wrapping in a tuple `[T]` is essential — bare `T extends never` distributes over an empty union and always returns `true` for `never` _after_ distribution, making the branch unreachable without the tuple wrapper.

```typescript
type IsNever<T> = [T] extends [never] ? true : false;
```

**Concepts:** `never` as an empty union, distributive conditional types, tuple wrapping to suppress distribution.

### AnyOf ⚡

Return `true` if any element in a tuple is truthy. Define a `Falsy` union, then recurse through the tuple — if the head isn't `Falsy`, short-circuit to `true`; if the tuple is exhausted, return `false`.

```typescript
type Falsy = "" | 0 | false | undefined | null | [];

type AnyOf<T extends unknown[]> = T extends [infer First, ...infer Tail]
  ? First extends Falsy
    ? AnyOf<Tail>
    : true
  : false;
```

**Concepts:** recursive conditional types, `infer` with rest, union membership check, falsy type modeling.

### Lookup

Extract a member from a union by matching a discriminant field. Distributing a conditional over the union lets each member check itself against the target literal.

```typescript
type LookUp<T, K extends string> = T extends { type: K } ? T : never;
```

**Concepts:** distributive conditional types, discriminated union filtering.

### Last

Return the last element of a tuple. Match a `[...Head, Tail]` pattern — the variadic head absorbs everything except the final element.

```typescript
type Last<T extends unknown[]> = T extends [...infer Head, infer Tail]
  ? Tail
  : never;
```

**Concepts:** `infer` with variadic rest in tail position, tuple destructuring.

### Capitalize

Uppercase only the first character of a string literal. Split the string into `First` + `Rest` with `infer`, apply the built-in `Uppercase` utility to `First`, then reassemble.

```typescript
type MyCapitalize<S extends string> = S extends `${infer First}${infer Rest}`
  ? `${Uppercase<First>}${Rest}`
  : S;
```

**Concepts:** template literal types, `infer` inside template literals, `Uppercase` intrinsic.

### TrimLeft

Strip leading whitespace (spaces, newlines, tabs) from a string literal. Recurse as long as the first character is a whitespace character.

```typescript
type TrimLeft<S extends string> = S extends `${infer First}${infer Rest}`
  ? First extends " " | "\n" | "\t"
    ? TrimLeft<Rest>
    : S
  : S;
```

**Concepts:** recursive template literal types, character-level `infer`.

### Replace

Replace the **first** occurrence of `From` in `S` with `To`. Guard against an empty `From` first, then use a three-part `infer` split around the target substring.

```typescript
type Replace<
  S extends string,
  F extends string,
  T extends string,
> = F extends ""
  ? S
  : S extends `${infer Head}${F}${infer Tail}`
    ? `${Head}${T}${Tail}`
    : S;
```

The empty-string guard is necessary because an empty `From` would match any string via a template literal split, producing an infinite or ambiguous result.

**Concepts:** template literal `infer` with a known middle segment, edge-case guarding.

### Deep Readonly ⚡

Recursively apply `readonly` to every property in a nested object. Check `keyof T[K] extends never` to detect primitives (functions, scalars) and leave them as-is; recurse into anything with keys.

```typescript
type DeepReadonly<T> = {
  readonly [K in keyof T]: keyof T[K] extends never ? T[K] : DeepReadonly<T[K]>;
};
```

**Concepts:** recursive mapped types, `readonly` modifier, primitive detection via `keyof`.

### Reverse

Reverse a tuple type. Peel the first element off with `infer`, recurse on the rest, then append the head at the end.

```typescript
type Reverse<T extends unknown[]> = T extends [infer First, ...infer Rest]
  ? [...Reverse<Rest>, First]
  : T;
```

**Concepts:** recursive variadic tuple types, `infer` with rest, tail-append pattern.

### IsUnion ⚡

Detect whether `T` is a union by exploiting how distributive conditionals behave. Keep a `Copy` of the original `T`, then distribute over `T` — for each member, check if it covers all of `Copy`. If yes for every member, `T` is not a union.

```typescript
type IsUnion<T, Copy = T> = (
  T extends Copy ? (Copy extends T ? true : false) : false
) extends true
  ? false
  : true;
```

For a union like `string | number`, distributing `T` yields individual members (`string`, then `number`). `Copy extends T` then checks `string | number extends string` — which is `false`, so the outer check fails, and `IsUnion` returns `true`. For a non-union, both extends checks succeed and the whole expression is `true`, so `IsUnion` returns `false`.

**Concepts:** distributive conditional types, union detection via distribution, `Copy` trick to preserve original union.

### IsAny ⚡

Detect whether `T` is `any`. `any` uniquely absorbs all intersections — `1 & any` collapses to `any`, making `0 extends 1 & T` true only when `T` is `any`.

```typescript
type IsAny<T> = 0 extends 1 & T ? true : false;
```

For any concrete type `T`, `1 & T` is either `1` (or a subtype) or `never`, so `0 extends 1 & T` is `false`. Only `any` makes the intersection widen back to `any`, satisfying `0 extends any`.

**Concepts:** `any` type absorption in intersections, structural quirks of `any`.