Utility Types page

Learn About the Utility Types provided by TypeScript

Overview

Utility Types are built-in helper Types meant for common Type transformations.

In this section you will see a small description for the most used Utility Types.

We are going to repeat the interface Dinosaur below throughout the examples.

enum Diet {
  "Carnivore",
  "Herbivore",
  "Omnivore",
}

interface Dinosaur {
  species: string;
  diet: Diet;
  age?: number; // the question mark signals that this property is optional
}

Partial<Type>

All properties of Type, but optional. Partial is often used when you need to partially update an object. See example below.

type AnotherDinosaur = Partial<Dinosaur>;

// AnotherDinosaur is equivalent to:
interface YetAnotherDinosaur {
  species?: string;
  diet?: Diet;
  age?: number;
}

// example
function updateDinosaur(
  dinosaur: Dinosaur,
  fieldsToUpdate: Partial<Dinosaur>
): Dinosaur {
  return { ...dino, ...fieldsToUpdate };
}

const oldDino: Dinosaur = {
  species: "Tyrannosaurus rex",
  diet: Diet.Carnivore,
};
const newDino: Dinosaur = updateDinosaur(dino1, {
  diet: Diet.Omnivore,
});

Required<Type>

All properties of Type, but required. For instance, you might use it when you are able to initialize all the properties of an object and want to avoid checking for null/undefined for the optional properties.

type AnotherDinosaur = Required<Dinosaur>;

// AnotherDinosaur is equivalent to:
interface YetAnotherDinosaur extends Dinosaur {
  age: number; // turning age property to required
}

const trex: AnotherDinosaur = {
  species: "Tyrannosaurus rex",
  diet: Diet.Carnivore,
  age: 30,
};

if (trex.age > 30) {
  // there is no need to check if age is null because it is a required property
}

Readonly<Type>

All properties of Type, but they are readonly. Use it to prevent objects from being mutated.

type NamableDinosaur = { name: string } & Dinosaur; // this is an intersection between { name: string } and Dinosaur. Think { name: string } + Dinosaur
type AnotherDinosaur = Readonly<NamableDinosaur>;

// Meet Bruno, another dinosaur
let dino: AnotherDinosaur = {
  name: "Bruno",
  age: 27,
  species: "Tyrannosaurus rex",
  diet: Diet.Carnivore,
};
dino.age = 0;

// Today is its birthday! Let`s attempt to increase its age:
dino.age += 1;
// Cannot assign to 'age' because it is a read-only property.
// Oops! TypeScript error

Record<Keys,Type>

Shortcut for defining properties keys and values. Particularly useful when you have a type in which multiple keys share the same value Type, so you could avoid repeating the pattern key: type;

let dinosCollection: Record<string, Dinosaur> = {
  // Record<string, Dinosaur> is equivalent to { [key: string]: Dinosaur }
  // Could also be written as Record<'trex' | 'triceratops', Dinosaur>
  trex: {
    species: "Tyrannosaurus rex",
    diet: Diet.Carnivore,
  },
  triceratops: {
    species: "Triceratops horridus",
    diet: Diet.Herbivore,
  },
};

Pick<Type, Keys>

Selects only the properties defined in Keys. Useful if you want a subset of Type

type LesserDinosaur = Pick<Dinosaur, "species" | "age">;

let lesserDino: LesserDinosaur = {
  species: "Tyrannosaurus rex",
  age: 27,
  // diet: Diet.Carnivore, <- if this line was present, then the error below would be thrown by typescript
  // Type '{ species: string; age: number; diet: Diet; }' is not assignable to type 'LesserDinosaur'.
  //  Object literal may only specify known properties, and 'diet' does not exist in type 'LesserDinosaur'.
};

Omit<Type, Keys>

Selects all properties but the ones defined in Keys. Useful if you want a subset of Type

type LesserDinosaur = Omit<Dinosaur, "species" | "age">;

const lesserDino: LesserDinosaur = {
  diet: Diet.Carnivore,
};

lesserDino.species = "Tyrannosaurus rex";
// Property 'species' does not exist on type 'LesserDinosaur'.
// species and age properties are gone!

Exclude<Type, ExcludedUnion>

Removes from Type if is assignable to Union. Useful if you want a subset of Type

type Species = "Tyrannosaurus rex" | "Triceratops horridus";

type SpeciesGone = Exclude<Species, "Triceratops horridus">; // SpeciesGone is Species minus Triceratops horridus

const dino: SpeciesGone = "Triceratops horridus";
// Type '"Triceratops horridus"' is not assignable to type '"Tyrannosaurus rex"'.
// Only rex remains now!

Extract<Type, Union>

Extracts from Type if is assignable to Union

type Species = "Tyrannosaurus rex" | "Triceratops horridus";

type SpeciesGone = Extract<Species, "Triceratops horridus">; // in this case, equivalent to type SpeciesGone = 'Triceratops horridus'

const dino: SpeciesGone = "Triceratops horridus";
// Only triceratops remains now!

// we can also extract common keys between 2 Types
interface Mammal {
  species: string;
  diet: Diet;
  weight: number;
}

type CommonKeys = Extract<keyof Mammal, keyof Dinosaur>;
// which is equivalent to:
// type CommonKeys = keyof Mammal & keyof Dinosaur;
// or:
// type CommonKeys = 'species' | 'diet'

NonNullable<Type>

Excludes null and undefined from Type. Prevent any runtime errors from occurring because we forgot to assign to a property.

type Species = "Tyrannosaurus rex" | "Triceratops horridus" | null | undefined;

type NNSpecies = NonNullable<Species>;
// equivalent to type NNSpecies = 'Tyrannosaurus rex' | 'Triceratops horridus'

ReturnType<Type>

Gets the type from the return type of a function Type

declare function getDinosaur(): Dinosaur;

type D1 = ReturnType<typeof getDinosaur>;
// type D1 = Dinosaur

type D2 = ReturnType<() => Dinosaur>;
// type D2 = Dinosaur

More on Utility Types

There are other built-in Utility Types:

  • Parameters<Type>

  • ConstructorParameters<Type>

  • InstanceType<Type>

  • ThisParameterType<Type>

  • OmitThisParameter<Type>

  • ThisType<Type>

  • Intrinsic String Manipulation Types

    • Uppercase<StringType>
    • Lowercase<StringType>
    • Capitalize<StringType>
    • Uncapitalize<StringType>

If you would like to dive deeper onto them, check the official documentation for TypeScript.

Exercise: Create a new Type from existing Type

The Problem

Update the 9-utility-types.ts in order to create a new Tyrannosaurus type that enforces diet property to be Diet.Carnivore and all Dinosaur properties to be required. Remember to take advantage of Utility Types and using existing Dinosaur Type when creating the new Tyrannosaurus Type.

interface Dinosaur {
  species: string;
  diet: Diet;
  age?: number;
}

enum Diet {
  "Carnivore",
  "Herbivore",
  "Omnivore",
}

type Tyrannosaurus = Dinosaur;

export { Diet, Tyrannosaurus };

Verify Your Solution

✏️ Run the following to verify your solution:

npm run 9-utility-types

The solution

Click to see the solution

✏️ Update 9-utility-types.ts to the following:

interface Dinosaur {
  species: string;
  diet: Diet;
  age?: number;
}

enum Diet {
  "Carnivore",
  "Herbivore",
  "Omnivore",
}

type Tyrannosaurus = Required<Dinosaur & { diet: Diet.Carnivore }>;

export { Diet, Tyrannosaurus };