Push your TypeScript expertise to the limit with these advanced challenges
A Vue-style defineComponent where this means the right thing in data, computed and methods: three inferred shapes fed back into the same object literal via ThisType.
Type a currying function that turns a multi-argument function into a chain of single-argument calls. Recursive tuple inference peels one parameter per step.
Turn A | B | C into A & B & C. There is no built-in operator for this; distributive conditionals and contravariant inference get you there.
Write GetRequired<T>, which drops every optional property from an object type. The filter runs inside the mapped type: as-remapping a key to never erases it.
Build GetOptional<T>, which keeps only the optional fields of an object type. Filtering keys is half the job; each surviving property must also keep its ? modifier.
Build RequiredKeys<T>, which collects every required key of an object type into a union. Checking for undefined fails; you have to test the ? modifier itself.
Build OptionalKeys<T>, which collects every optional key of an object type into a union. The hard part: optionality is a modifier, so checking for undefined gets you nowhere.
Uppercase the first letter of every word in a string type, where a word boundary is any character without an upper or lower case form.
Convert a snake_case string type to camelCase with recursive template literal inference. The tricky part: characters that cannot be uppercased, like _ and $.
Parse a C-style printf format string at the type level, collecting placeholders like %d into a tuple. Lazy template inference handles %% escapes for free.
Extend Simple Vue with a props option: infer prop types from constructors like Boolean, String or custom classes, including unions from constructor arrays.
Detect the any type, and only any. Every obvious check produces false positives; the working one-liner is 0 extends 1 & T, and the why behind it is the real lesson.
A fully typed version of lodash's get: parse a dot-separated path string with template literal inference and resolve it to the exact value type.
Convert a string literal type to its number, like Number.parseInt but stricter. One infer extends clause replaces pages of digit-by-digit recursion.
FilterOut<T, F> removes tuple elements assignable to F. The catch is never, which makes naked conditional checks silently evaporate.
Convert a string tuple into a readonly enum-like object: PascalCase keys, filtered array keys, and index strings turned back into number literals.
Format<T> turns a printf-style format string into a curried function type, one typed argument per placeholder. The twist: the recursion builds function types, not tuples.
Brand an object and every nested object with its own identity while staying mutually assignable with the original. Nominal typing built by hand.
Count the length of a string type with a tuple accumulator. The naive recursion dies at about 45 characters; tail recursion takes you to 999.
Convert a union into a tuple even though unions have no order. The key move is extracting a single member via function overload resolution.
A curried join function whose return type is the exact joined string literal, computed from the delimiter and arguments by a recursive template literal type.
A Pick that understands dot paths like friend.family.name, building one nested pick per path and folding them into an intersection via contravariant inference.
Type Pinia's defineStore: getters are written as functions but read as values, and state is mutable in actions but read-only in getters. ThisType does the work.
Rename every snake_case key in an object type to camelCase, recursing through nested objects and tuples. The array branch must come before the object branch.
Remove every character of R from the string type S. One recursion turns R into a union of characters, another walks S and filters against it.
A type-level String.split() that turns a string type into a tuple of substrings. Reproducing JavaScript's odd empty-string rules is the hard part.
Extract the public keys of a class into a union. The answer is one line, because keyof never sees private or protected members in the first place.
IsRequiredKey<T, K> reports whether every key in K is required on T. Optionality is a modifier, not a value type, so the check compares Pick against Required.
Turn a union of [key, value] tuples into an object type, the type-level Object.fromEntries. One mapped type does it, once you know in can iterate any union.
Check whether a string or number reads the same backwards, entirely in types. A recursive template-literal split does the reversal.
Build MutableKeys<T>, a union of the non-readonly keys of an object type. Assignability checks ignore readonly, so detecting it takes the Equal trick.
The type-level version of Lodash intersection: compute what several lists have in common. Normalize every entry to a union and let & do the set math.
Convert a binary string literal like '1010' into the number type 10. The type system has no arithmetic, so tuples stand in for numbers and spreads do the math.
Generate every path string lodash _.get accepts on an object, dot and bracket notation included, by walking the type with recursive template literals.
Solve Two Sum in the type system, where there is no + operator and no loop. Two nested recursions over a tuple, with addition built from tuple lengths.
Implement ValidDate<T>, which checks whether an MMDD string is a real calendar date. Every legal month and day gets enumerated as a union of string literals.
Recreate Object.assign at the type level: merge an array of sources into a target, later sources winning. Intersections collapse conflicts to never, so they're out.
Find the largest number in a tuple type with no greater-than operator in sight. A counting tuple eliminates union members until only the maximum remains.
Capitalize every key of an object type recursively, following values into nested arrays. The branch order decides whether tuples survive the mapping.
UnionReplace<T, U> rewrites a union according to a list of [from, to] type pairs: one conditional distributes over the members, another recurses through the pairs.
Generate the FizzBuzz sequence as a tuple of string literals, without loops or a modulo operator. Tuple counters and a tail-recursive accumulator do the work.
Encode and decode run-length compressed strings at the type level, combining template literal inference, string accumulators, tuple counters and a digit trick.
Path<T> computes every valid key path through a nested object as a union of tuples. The machinery behind type-safe lodash.get and form field paths.
SnakeCase<T> converts camelCase string types to snake_case by scanning one character at a time. Union inputs work for free thanks to distributive conditionals.
IsNegativeNumber<N> returns true for negative literals, false for zero and positives, and never for number or unions. Union detection is the tricky part.
Make every property that allows undefined optional, restricted to a chosen key set. Split the object with key remapping, add the ? modifier, merge back.
Write uniqueItems, a function that rejects tuples with duplicate elements and pins the compiler error on each repeated element instead of the whole argument.
XOR two binary string literals of different lengths. Bitwise ops align at the right end, the one end template literal inference cannot reach, so you reverse first.
A type that verifies a solved Sudoku board: 27 region checks built from indexed access on tuples and one union-as-set comparison.
Count string lengths up to a million characters at the type level. Fixed-width template patterns strip 100,000 characters per match; the counts become digits.
Build Unbox<T>, which unwraps functions, promises, arrays and tuples, recursively or to a chosen depth. Fixpoint recursion meets a type-level counter.
Add two binary numbers, given as bit tuples, by modeling a full adder: a sum and a carry bit per column, carry rippling left. Tuple-length counting tricks are banned.
Filter a union of object types down to the members that have a given key. No recursion, no infer: the whole difficulty is understanding distribution.
Take<N, Arr> extracts the first N elements of a tuple, or the last N when N is negative. Accumulator counting and template literal sign detection do the work.
Grade a finished 9x9 Sudoku grid entirely in the type system. Indexed access over unions carves out rows, columns and boxes without any tuple slicing.
Ready for the ultimate challenge? Try our extreme challenges or browse all TypeScript challenges!