Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Generic

keyof Type Operator

The keyof operator takes an object type and produces a string or numeric literal union of its keys.

The following type P is the same type as type P = "x" | "y":

type Point = { x: number; y: number };
type P = keyof Point;

Another example:

type Arrayish = { [n: number]: unknown };
type A = keyof Arrayish;

//type A = number;

Indexed Access types

We can use an indexed access type to look up a specific property on another type:

type Person = { age: number; name: string; alive: boolean };
type Age = Person["age"];

//type Age = number;

The indexing type is itself a type, so we can use unions, keyof, or other types entirely:

type I1 = Person["age" | "name"];

type I1 = string | number;

type I2 = Person[keyof Person];

type I2 = string | number | boolean;

type AliveOrName = "alive" | "name";
type I3 = Person[AliveOrName];

type I3 = string | boolean;

Another example of indexing with an arbitrary type is using number to get the type of an array’s elements. We can combine this with typeof to conveniently capture the element type of an array literal:

const MyArray = [
  { name: "Alice", age: 15 },
  { name: "Bob", age: 23 },
  { name: "Eve", age: 38 },
];

type Person = (typeof MyArray)[number];

// type Person = {
//   name: string;
//   age: number;
// };

Mapped types

A mapped type is a generic type which uses a union of PropertyKeys (frequently created via a keyof) to iterate through keys to create a type:

type OptionsFlags<Type> = {
  [Property in keyof Type]: boolean;
};

type Features = {
  darkMode: () => void;
  newUserProfile: () => void;
};

type FeatureOptions = OptionsFlags<Features>;

// type FeatureOptions = {
//     darkMode: boolean;
//     newUserProfile: boolean;
// }

Mapping modifiers

There are two additional modifiers which can be applied during mapping: readonly and ? which affect mutability and optionality respectively.

You can remove or add these modifiers by prefixing with - or +. If you don’t add a prefix, then + is assumed.

// Removes 'readonly' attributes from a type's properties
type CreateMutable<Type> = {
  -readonly [Property in keyof Type]: Type[Property];
};

type LockedAccount = {
  readonly id: string;
  readonly name: string;
};

type UnlockedAccount = CreateMutable<LockedAccount>;

type UnlockedAccount = {
  id: string;
  name: string;
};

Generic Constraints

Typescript cannot narrow down the property type in this case:

const testObj = { x: 10, y: "Hello", z: true };

function getProperty<T>(obj: T, key: keyof T) {
  return obj[key];
}

const xValue = getProperty(testObj, "x");
// const xValue: string | number | boolean

const yValue = getProperty(testObj, "y");
// const yValue: string | number | boolean

Use extends as generic Constraints

const testObj = { x: 10, y: "Hello", z: true };

function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

const xValue = getProperty(testObj, "x");
// const xValue: number
const yValue = getProperty(testObj, "y");
// const yValue: string

Conditional types

type IsString<T> = T extends string ? true : false;