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.
Turn a union of component variants into a lookup table keyed by its discriminant, and constrain the generic so a key the union does not have is a compile error.
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.
Turn an object type into a tuple of its value types. The hard part is converting a union of keys into an ordered tuple, which the type system does not hand you.
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.
Transpose a 2d tuple whose rows have different lengths, including rows and matrices written with variadic rest elements, and trim every ragged edge.
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.
Find the discriminant key of a tagged union: the property every member shares with a distinct string literal value. Catching duplicate tags is the hard part.
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.
Collect the parameters of every overload of a function type into a union of tuples, padded with never so destructuring the result stays narrowable.
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.
Cut a tuple of length 3n into three equal parts. Peeling one element off each end per step makes both cut points appear without any arithmetic.
Return 1, -1 or 0 for two numeric literal types. The digits only reach you as a string, so signs, decimal points and scientific notation are yours to handle.
Decide at the type level whether a tuple holds exactly one element of every member of a string union, using a recursive walk and Exclude as a checklist.
Turn any safe integer into its binary string at the type level. Counting tuples cannot reach 2^53, so the solution halves a decimal string digit by digit.
Write a function that accepts every argument type except never. No parameter type can reject never, so the rejection has to move into the argument count.
Track which resources a fluent chain has already spent inside the type system, so calling a method whose dependency is gone becomes a compile error.
Build the union of every non-empty subset of an object's keys, with each variant flat and distinct, and keep it cheap enough to survive a 17-key record.
Fold a union of nested objects and tuples into one structure whose leaves carry the unions. The hard part is stopping mapped types from distributing.
Ready for the ultimate challenge? Try our extreme challenges or browse all TypeScript challenges!