Comprehensive list of built-in utility types in TypeScript

miloszpp

Milosz Piechocki

Posted on June 30, 2019

Comprehensive list of built-in utility types in TypeScript

Advanced types section of TypeScript docs mentions some very useful built-in types as examples of conditional types and mapped types.

I was surprised to learn that there are more such types and some of them seem to be undocumented. This article contains a list of all such types.

The list is based on what I could find in es5.d.ts on github.

List of types

Partial

Partial<T> returns a type that has the same properties as T but all of them are optional. This is mostly useful when strictNullChecks flag is enabled.

Partial works on a single level - it doesn't affect nested objects.

A common use case for Partial is when you need to type a function that lets you override default values of properties of some object.

const defaultSettings: Settings = { /* ... */ };

function getSettings(custom: Partial<Settings>): Settings {
  return { ...defaultSettings, ...custom };
}

Update:

However, this technique is not 100% type-safe. As pointed out by AngularBeginner, if custom has a property that has been explicitly set to undefined, the result will end up having this property undefined as well. Therefore, its type (Settings) will be a lie.

A more type-safe version of getSettings would look like this:

function getSettings(custom: Partial<Settings>): Partial<Settings> {
  return { ...defaultSettings, ...custom };
}

See implementation.

Required

Required<T> removes optionality from T's properties. Again, you'll most likely need it if you have strictNullChecks enabled (which you should šŸ˜‰).

Similarly to Required, Partial works on the top level only.

The example is somehow symmetrical to the previous one. Here, we accept an object that has some optional properties. Then, we apply default values when a property is not present. The result is an object with no optional properties - Required<Settings>.

function applySettings(settings: Settings) {
  const actualSettings: Required<Settings> = {
    width: settings.width || 100,
    height: settings.height || 200,
    title: settings.title || '',
  }
  // do something...
}

See implementation.

Readonly

This one you probably have heard of. Readonly<T> returns a type that has the same properties as T but they are all readonly. It is extremally useful for functional programming because it lets you ensure immutability at compile time. An obvious example would be to use it for Redux state.

Once again, Readonly doesn't affect nested objects.

See implementation.

Pick

Pick lets you create a type that only has selected properties of another type.

An example would be letting the caller override only a specific subset of some default properties.

function updateSize(overrides: Pick<Settings, 'width' | 'height'>) {
  return { ...defaultSettings, ...overrides};
}

See implementation.

Record

Record lets you define a dictionary with keys belonging to a specific type.

JavaScript objects can be very naturally used as dictionaries. However, in TypeScript you usually work with objects using interfaces where the set of keys is predefined. You can work this around by writing something like:

interface Options {
  [key: string]: string;
}

Record lets you do this in a more concise way: type Options = Record<string, string>.

See implementation.

Exclude

Exclude makes a lot of sense when you look at types in terms of sets of possible values. For example, number type can be looked at as a set containing all numerical numbers. A | B is called a union because its set of possible values is a sum of possible values of A and possible values of B.

Exclude<T, U> returns a type whose set of values is the same as the set of values of type T but with all U values removed. It is a bit like substruction, but defined on sets.

A good example of using Exclude is to define Omit. Omit takes a type and its key and returns a type without this key.

interface Settings {
  width: number;
  height: number;
  title: string;
}

type SizeSettings = Omit<Settings, 'title'>;

// type SizeSettings = {
//   width: number;
//   height: number;
// }

Omit<T, K> can be defined by picking all keys from T except K. First, we'll Exclude K from the set of keys of T. Next, we will use this set of keys to Pick from T.

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

See implementation.

Extract

Extract<T, U> return those types included in T that are assignable to U. You can say that it returns a common part of T and U. However, the types don't have to be exactly the same - it suffices that a type from T is assignable to U.

For example, you can use Extract to filter out function types from a union type:

type Functions = Extract<string | number | (() => void), Function>;  // () => void

See implementation.

NonNullable

NonNullable<T> removes null and undefined from the set of possible values of T.

It is mostly useful when working with strictNullChecks and optional properties and arguments. It has no effect on a type that is already not nullable.

type Foo = NonNullable<string | null | undefined>; // string

You can find a good usage example of NonNullable in my previous article.

See implementation.

Parameters

This useful type returns a tuple of types of parameters of given function.

function fetchData(id: number, filter: string) {
}

type FetchDataParams = Parameters<typeof fetchData>; // [number, string]

type IdType = FetchDataParams[0]; // number

One interesting usage is typing wrapper functions without having to repeat the parameter list.

function fetchDataLogged(...params: Parameters<typeof fetchData>) {
  console.log('calling fetchData');
  fetchData(...params);
}

See implementation.

ConstructorParameters

ConstructorParameters is exactly the same as Parameters but works with constructor functions.

class Foo {
  constructor(a: string, b: number) {}
}

type FooConstructorParams = ConstructorParameters<typeof Foo>;

One caveat is that you have to remember about typeof in front of the class name.

See implementation.

ReturnType

The name is pretty self-explanatory - it returns a type returned by given function. I found this type really useful.

One example is in Redux where you define action creators and reducers. A reducer accepts a state object and an action object. You can use ReturnType of the action creator to type the action object.

function fetchDataSuccess(data: string[]) {
  return {
    type: 'fetchDataSuccess',
    payload: data
  }
}

function reduceFetchDataSuccess(state: State, { payload }: ReturnType<typeof fetchDataSuccess>) {
}

See implementation.

InstanceType

InstanceType is an interesting one. You can say that it is complimentary to typeof operator.

It accepts a type of a constructor function and returns an instance type of this function.

In TypeScript, class C defines two things:

  • a constructor function C for creating new instances of class C
  • an interface for objects of class C - the instance type

typeof C returns the type of the constructor function.

InstanceType<typeof C> takes the constructor function and returns type of the instances produced by this function: C.

class C {
}

type CInstance = InstanceType<typeof C>;  // C

See implementation.

Want to learn more?

Did you like this TypeScript article? I bet you'll also like my course!

ā­ļø Advanced TypeScript Masterclass ā­ļø

šŸ’– šŸ’Ŗ šŸ™… šŸš©
miloszpp
Milosz Piechocki

Posted on June 30, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related