Last updated: Apr 29, 2026
TypeScript Challenges
Work in Progress — A collection of solutions to TypeScript type challenges sourced from BFE.dev TypeScript, type-challenges, and TypeScript Cookbook.
Key Techniques
| Technique | Use case |
|---|---|
Mapped types [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 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 ...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.
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.
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.
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.
type First<T extends any[]> = T["length"] extends 0 ? never : T[0];
Alternative using infer:
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.
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.
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.
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.
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.
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.
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.
type Push<T extends unknown[], U> = [...T, U];
Concepts: variadic tuple types.
Unshift
Prepend an element to a tuple using spread.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.