Advanced

Lookup Types and Index Access

Access nested and indexed types with `keyof`, index access types, and template literal types.

What you will see

a-b-c

About 2 min of reading

Try It

Lookup and index-access types compute at compile time from a source type's keys and values. The equivalent runtime work reads property names and values from real objects.

1

keyof and index access

const user = { name: "Ada", age: 30 }; const keys = Object.keys(user); console.log(keys, user["name"]);

Before you run it, say out loud what it will print. Then open the answer.

What it prints
[ 'name', 'age' ] Ada
2

Generic index access

function ValueOf(obj) { return Object.values(obj); } console.log(ValueOf({ host: "localhost", port: 8080 }));

Before you run it, say out loud what it will print. Then open the answer.

What it prints
[ 'localhost', 8080 ]
3

Template literal types

function CamelToSnake(str) { return str.replace(/_(.)/g, (m, c) => "-" + c.toLowerCase()); } console.log(CamelToSnake("a_b_c"));

Before you run it, say out loud what it will print. Then open the answer.

What it prints
a-b-c

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

The compiler inspects a type's keys and values, computing new types from existing ones at build time.
1 Reference a type with keyof or index access
↓
2 Compiler reads the property names
↓
3 Extract each property's value type
↓
4 Emit a computed type for later use
Why this works
Why lookup types matter

They keep types in sync with their sources without repetition:

const keys = ["host", "port"] as const;
type Key = typeof keys[number];  // "host" | "port"

typeof keys[number] reads the element type of a tuple. This pattern derives a type directly from a real value, so renaming a key cannot desync the type.

Mapped + lookup combos

Combine lookup with mapped types for powerful transforms:

type PromiseAll<T> = {
  [K in keyof T]: Promise<T[K]>;
};
interface Models { user: User; post: Post; }
type AsyncModels = PromiseAll<Models>; // { user: Promise<User>; ... }

This wraps every property in a Promise, iterating the source's keys. It is exactly how Promise.all's type is expressed.

Examples

keyof and index access

Extract property types

interface User {
  name: string;
  age: number;
}
type NameType = User["name"];   // string
type Keys = keyof User;          // "name" | "age"

keyof User gives the union of property names; User["name"] gives that property's type. These are computed at compile time.

Generic index access

ValueOf helper

type ValueOf<T> = T[keyof T];
interface Config { host: string; port: number; }
type Values = ValueOf<Config>;  // string | number

Indexing with keyof T across all keys yields the union of every value type. Useful for building flexible APIs.

Template literal types

String manipulation in types

type CamelToSnake<T extends string> =
  T extends `${infer A}_${infer B}` ? `${A}-${B}` : T;
type X = CamelToSnake<"a_b_c">;  // "a-b-c"

infer extracts parts of a string type at compile time. Template literal types let you transform string literal types programmatically.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Decorators
Previous Lesson
Decorators
Next Lesson
Exhaustiveness Checking
Exhaustiveness Checking →