TypeScript path · Types from types · Topic 7 of 11

keyof, mapped and conditional types

Not started

TypeScript has a small language for computing types from other types. keyof, typeof and indexed access read types off existing values and types; mapped types transform every key of an object type; conditional types choose between types and can extract parts of them. It all runs in the checker and disappears from the output. Used lightly, it removes duplication: one definition, with the rest derived from it. Used heavily, it produces types nobody on the team can read.

Why it matters: The built-in helpers like `Partial`, `Pick` and `ReturnType` are made of these pieces. Knowing them lets you read library types and derive yours from one source of truth, instead of copying them.

  1. 1Read types off values and types

    `typeof value` gives a value's type, `keyof T` the union of its keys, and `T[K]` the type at a key. Together they let one definition drive the others.

  2. 2Mapped types transform every key

    `{ [K in keyof T]: ... }` builds a new object type key by key. `Partial`, `Readonly`, `Pick` and `Record` are all mapped types.

  3. 3Conditional types choose

    `T extends U ? X : Y` picks a type by testing another, and `infer` pulls a piece out of it. `ReturnType` and `Awaited` work this way.

  4. 4Use the built-ins first

    Type-level code is hard to read and its errors are worse. Reach for the standard utility types, and write your own only when they save real duplication.

1

How it works

The explanation, with examples you can run.

Reading types off what exists

const sizes = { s: 8, m: 12, l: 16 } as const type Size = keyof typeof sizes // "s" | "m" | "l"type Px = (typeof sizes)[Size] // 8 | 12 | 16 function px(size: Size): number {  return sizes[size]}px("m")px("xl") // Error: Argument of type '"xl"' is not assignable to parameter of type '"s" | "m" | "l"'

The object is the single source of truth. Add a size and Size grows with it, with no separate list to keep in sync.

Mapped types

A mapped type walks over a union of keys and builds a property for each:

type Flags<T> = { [K in keyof T]: boolean } type User = { name: string; email: string; age: number }type Touched = Flags<User> // { name: boolean; email: boolean; age: boolean }

Modifiers can be added or removed (?, readonly, -?), and keys can be renamed with as. Most of what you need is already built in:

UtilityGives you
Partial<T> / Required<T>Every property optional / required
Readonly<T>Every property readonly (checked only, not frozen at runtime)
Pick<T, K> / Omit<T, K>Only the keys K / every key except K
Record<K, V>An object with keys K and values V
ReturnType<F> / Parameters<F>A function type's result / parameter tuple
Awaited<T>What a promise resolves to

Conditional types and infer

type ElementOf<T> = T extends (infer E)[] ? E : never type A = ElementOf<string[]> // stringtype B = ElementOf<number> // never async function loadUser() {  return { id: "u1", name: "Ada" }}type LoadedUser = Awaited<ReturnType<typeof loadUser>> // { id: string; name: string }

T extends U ? X : Y asks whether T fits U. infer E names a part of the match so the result can use it. When T is a union, the condition is applied to each member separately (it distributes), which is how Exclude<"a" | "b", "a"> gives "b".

Awaited<ReturnType<typeof loadUser>> is the kind of derived type worth writing: the function stays the source of truth, and the type follows it.

Restraint

Each layer of type-level code makes errors longer and the editor slower, and it can only be tested by the compiler. Before writing one, ask whether a plain type alias would do. Prefer the built-in utilities, give derived types names, and keep clever types in one shared file rather than spread through application code.

2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.`const sizes = { s: 8, m: 12, l: 16 }`. What is `keyof typeof sizes`?
  2. 2.What does `Partial<User>` produce?
  3. 3.What is `ElementOf<string[]>` with this definition?
    type ElementOf<T> = T extends (infer E)[] ? E : never

0 of 3 right so far. Get all three to finish this topic.