skip to content

Utility & Mapped Types

This is TypeScript's type-level transformation layer: deriving new types from existing ones instead of re-declaring shapes by hand. Interviewers use it to separate people who consume types from people who can compute them — from Partial and Omit up through mapped, conditional, and template literal types.

part ofTypeScriptoverview, primer and where to startread it →
on this pageshow

explore

questions

63 · 6 sections

In TypeScript, what do the built-in utility types `Exclude<T, U>`, `Extract<T, U>` and `NonNullable<T>` produce when you apply them to a union type?

level: juniorimportance: must knowfreq 72%
basics
~10 s

They are set operations over unions. Exclude<T, U> drops every member of T assignable to U, Extract<T, U> keeps only those members, and NonNullable<T> removes null and undefined from T.

open as a page

In TypeScript, a function `createUser` returns an object literal whose shape you never declared. How do you get a named type for that return value without writing the shape twice, and why does `ReturnType<createUser>` fail to compile?

level: juniorimportance: must knowfreq 72%
basics
~10 s

ReturnType<typeof createUser> gives the type. In a type position, typeof turns the value createUser into its function type, which ReturnType then unwraps. ReturnType<createUser> fails because createUser is a value, not a type.

open as a page

In TypeScript, what does each of the built-in utility types `Partial<T>`, `Required<T>` and `Readonly<T>` do to the properties of T?

level: juniorimportance: must knowfreq 78%
basics
~20 s

Partial<T> makes every property of T optional, Required<T> makes every property mandatory, and Readonly<T> makes every property read-only. All three keep T's keys and property types, flip one modifier, and disappear when the code is compiled to JavaScript.

open as a page

In TypeScript, what do `Pick<T, K>` and `Omit<T, K>` produce, and why derive a type with them instead of hand-writing a second interface?

level: juniorimportance: must knowfreq 72%
basics
~20 s

Pick<T, K> builds an object type containing only the properties of T named in K; Omit<T, K> contains every property of T except those. Deriving keeps one source of truth, so editing T updates both types automatically.

open as a page

In TypeScript, what object type does the built-in `Record<K, V>` utility produce, and how does its mapped-type definition explain the difference between `Record<string, number>` and `Record<'a' | 'b', number>`?

level: juniorimportance: must knowfreq 72%
basics
~20 s

Record<K, V> builds an object type whose keys come from K and whose values are all V. It is defined as the mapped type { [P in K]: V }, so a string key produces an open index signature while a union of literals produces exactly those required properties.

open as a page

In TypeScript, what does the `keyof` operator produce for an object type such as `interface User { id: number; name: string }`, and how do you apply it to a value like `const config = { host: 'localhost', port: 5432 }`?

level: juniorimportance: must knowfreq 75%
basics
~20 s

keyof produces the union of a type's property keys as literal types: keyof User is "id" | "name". It operates on types only, so for a value you write keyof typeof config. Nothing of it survives compilation.

open as a page

In TypeScript, given `interface User { id: number; name?: string }` and `const roles = ['admin', 'user'] as const`, what types do the indexed access types `User['id']`, `User[keyof User]` and `(typeof roles)[number]` produce?

level: middleimportance: must knowfreq 58%
basics
~20 s

Indexed access reads a property's type by key: User['id'] is number, User[keyof User] is the union of all property types (number | string | undefined), and (typeof roles)[number] is the union of the tuple's elements, 'admin' | 'user'.

open as a page

In TypeScript, how do you write a `Mutable<T>` mapped type that strips `readonly` from every property of `T`, and what do the `+` and `-` prefixes mean in a mapped type?

level: middleimportance: must knowfreq 52%
basics
~20 s

Write type Mutable<T> = { -readonly [K in keyof T]: T[K] }. In a mapped type the minus prefix strips a modifier the source property carried and plus adds one; both prefixes apply to readonly and to the optional marker ?.

open as a page

In TypeScript, given `type A = { id: number; name: string }` and `type B = { id: number; age: number }`, what is `keyof (A | B)`, and what rule produces that answer?

level: middleimportance: should knowfreq 42%
basics
~20 s

keyof (A | B) is "id" — only the keys every member has. keyof distributes over the union and the results are intersected, because a value of a union type is guaranteed to carry just the shared properties. The mirror case: keyof (A & B) is "id" | "name" | "age".

open as a page

In a TypeScript mapped type, what does the `as` clause in `{ [K in keyof T as NewKey]: T[K] }` do, and what happens to a key whose `as` clause evaluates to `never`?

level: middleimportance: should knowfreq 45%
basics
~20 s

The as clause renames each key while the mapped type iterates, emitting the property under the computed name instead of K. A key whose clause evaluates to never produces no property at all, which is how mapped types filter keys out.

open as a page

In TypeScript, what does the type `type IsString<T> = T extends string ? 'yes' : 'no'` do, and what does `IsString<'abc'>` resolve to?

level: juniorimportance: must knowfreq 62%
basics
~20 s

A conditional type chooses between two types by testing assignability: if T is assignable to string the type becomes 'yes', otherwise 'no'. IsString<'abc'> resolves to 'yes', because the literal type 'abc' is assignable to string.

open as a page

In TypeScript, given `type ToArray<T> = T extends unknown ? T[] : never`, why does `ToArray<string | number>` evaluate to `string[] | number[]` rather than `(string | number)[]`?

level: middleimportance: must knowfreq 62%
basics
~20 s

A conditional type whose checked type is a bare type parameter distributes: TypeScript applies it to each union member separately and unions the results. ToArray runs once on string and once on number, producing string[] | number[].

open as a page

In a TypeScript conditional type, `extends` is often described as "assignable to" rather than "inherits from". What does `type R = { id: number; name: string } extends { id: number } ? 'yes' : 'no'` resolve to, and why?

level: middleimportance: must knowfreq 58%
basics
~20 s

The result is 'yes'. In a conditional type, extends asks whether the left type is assignable to the right one, and an object type with an extra property is assignable to one that requires fewer. No declared inheritance is involved.

open as a page

In a TypeScript conditional type, what does the `infer` keyword do, where may the variable it declares be used, and how would you write an `ElementType<T>` that pulls the element type out of an array type?

level: middleimportance: must knowfreq 62%
basics
~20 s

infer declares a type variable inside the extends clause of a conditional type, letting the compiler pattern-match a shape and capture whatever sits in that slot. The captured variable is in scope only in the true branch.

open as a page

In TypeScript, `type IsString<T> = T extends string ? true : false` gives `boolean` for `IsString<string | number>`. Explain that result and how you would make the check apply to the whole union instead.

level: middleimportance: should knowfreq 50%
basics
~20 s

The conditional distributes over the union: string yields true, number yields false, and true | false is exactly how boolean is defined. Wrapping both sides in one-element tuples, [T] extends [string], disables distribution and returns false.

open as a page

In TypeScript, what values does the type `type Greeting = `hello ${string}`` accept, and how is it different from plain `string`?

level: juniorimportance: should knowfreq 45%
basics
~20 s

Any string that begins with "hello " — the placeholder matches any string, so "hello world" is assignable but "hi" is not. The type is a narrower subtype of string, and the match is checked only at compile time.

open as a page

In TypeScript, given `type Shade = 'light' | 'dark'` and `type Color = 'red' | 'blue'`, what type is `` `${Shade}-${Color}` `` and what rule produces it?

level: middleimportance: should knowfreq 55%
basics
~10 s

It is the four-member union "light-red" | "light-blue" | "dark-red" | "dark-blue". A union placed in a placeholder distributes, so the result is the cross-product of every placeholder's members.

open as a page

In TypeScript, what does `type Tail<S> = S extends `${infer Head}-${infer Rest}` ? Rest : never` give for `Tail<'a-b-c'>`, and what matching rule explains it?

level: seniorimportance: should knowfreq 35%
basics
~10 s

It gives "b-c". A placeholder followed by literal text matches as little as possible, so Head captures "a" at the first hyphen and Rest absorbs everything after it, separators included.

open as a page

A codebase types its navigation helper as `function navigate(path: `/users/${string}`): void`. What does that annotation actually guarantee, and where does it stop helping?

level: seniorimportance: should knowfreq 32%
basics
~20 s

It only constrains call sites whose argument still carries a literal or template literal type. A value typed plain string is rejected until someone asserts it, and nothing is verified at runtime because the type is erased.

open as a page

Which four string-manipulation types does the TypeScript compiler provide as built-ins, what does each do, and why can you not implement them yourself in the type system?

level: middleimportance: nice to knowfreq 38%
basics
~10 s

Uppercase, Lowercase, Capitalize and Uncapitalize. Each transforms a string literal type, and they are declared as intrinsic in lib.es5.d.ts, meaning the compiler implements them natively because the type system has no character-level string operation.

open as a page

In TypeScript, how do you write a single type that describes any value `JSON.parse` can return, including arbitrarily deep nesting, and why is a type alias allowed to name itself?

level: juniorimportance: should knowfreq 42%
basics
~20 s

Use a recursive type alias: a JSON value is a string, number, boolean, null, an array of JSON values, or an object whose values are JSON values. The self-reference is legal because TypeScript resolves it lazily instead of expanding it eagerly.

open as a page

A codebase defines `type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K] }` and applies it to a type with `string[]`, `Date`, and callback properties. What goes wrong, and how do you harden it?

level: middleimportance: should knowfreq 50%
basics
~20 s

The extends object test also matches arrays, Dates, Maps and functions, so the helper recurses into them: array elements gain undefined, a Date becomes an object of optional methods, and a function loses its call signature. Fix it by bailing out on those shapes before the object branch.

open as a page

How would you write a recursive type that produces the union of dotted key paths ("user.address.city") for a nested object type, and what breaks when the object contains arrays or a self-referencing node?

level: seniorimportance: nice to knowfreq 24%
basics
~20 s

Recurse over the keys, emitting each key and, for object-valued keys, that key joined to the child's paths with a dot. Arrays leak Array's own member names into the union, and a self-referencing type recurses forever until the compiler reports an excessive-depth error.

open as a page

What does TypeScript's error "Type instantiation is excessively deep and possibly infinite" mean, and what do you change in a recursive conditional type to get past it?

level: seniorimportance: nice to knowfreq 28%
basics
~20 s

The checker hit its cap on nested type instantiations while expanding a recursive type and gave up rather than hang. Fix it by adding or correcting a base case, rewriting the recursion with an accumulator so the recursive call sits in tail position, or capping depth with a counter.

open as a page

In TypeScript, why can type-only helpers slow `tsc` and editor IntelliSense down noticeably, and which kinds of types tend to be the expensive ones?

level: middleimportance: must knowfreq 50%
basics
~20 s

The checker does real work per generic instantiation and per assignability comparison, and the editor's TypeScript server repeats it while you type. Large unions, deeply nested conditionals, long intersection chains and inferred-instead-of-annotated return types are the usual expensive shapes.

open as a page

Do elaborate TypeScript types — deeply nested conditional and mapped helpers — make the shipped JavaScript slower, and if not, where does their cost actually land?

level: juniorimportance: should knowfreq 45%
basics
~20 s

No — TypeScript erases types, so even elaborate helpers emit no JavaScript and cost nothing at run time. Their price is paid at check time: slower tsc runs, laggy editor completions, and error messages teammates struggle to read.

open as a page

A generic TypeScript helper produces an error message that runs for pages and is cut off with `...`. Why do computed types fail this way, and how do you get the errors under control?

level: middleimportance: should knowfreq 42%
basics
~20 s

The checker reports the failure with both sides fully expanded at the outermost place it noticed, so a computed type prints as its whole structure and gets truncated. Control it by naming intermediate aliases, constraining parameters, testing types in isolation, and using --noErrorTruncation when you need the full text.

open as a page

A TypeScript project's `tsc --noEmit` has crept from twenty seconds to four minutes with no new dependencies. How do you find which types are responsible?

level: seniorimportance: should knowfreq 38%
basics
~20 s

Measure before rewriting anything. Run tsc with --extendedDiagnostics to see whether check time and instantiation counts dominate, then --generateTrace into a directory and analyse the trace with @typescript/analyze-trace to get the specific files and positions that are slow.

open as a page

As the lead on a shared TypeScript codebase, how do you decide how much type-level complexity to allow, and when should the guarantee move to a runtime validator such as Zod instead?

level: principalimportance: should knowfreq 33%
basics
~20 s

Judge each clever type by guarantee per unit of cost: build and editor latency, plus how many teammates can maintain it. Anything about data entering the program from outside cannot be guaranteed by types at all and belongs in a runtime validator, with the static types derived from that schema.

open as a page