Mastering TypeScript Utility Types: A Comprehensive Guide

Mastering TypeScript Utility Types: A Comprehensive Guide

Β·

5 min read

Welcome, fellow developers, to the captivating world of TypeScript utility types! TypeScript equips us with a toolbox of utility types to simplify complex type manipulations. Let's dive right in, understanding their essence with crisp definitions and insightful examples.

Partial<Type>: Embracing Optionality

With Partial, all properties of a type become optional, offering flexibility in handling optional data.

interface User {
  name: string;
  age?: number;
}

function updateUser(user: User, updates: Partial<User>): User {
  return { ...user, ...updates };
}

const initialUser: User = { name: "Vroon" };
const updatedUser = updateUser(initialUser, { age: 20 });

Awaited<Type>: Unraveling Promises

The Awaited type unwraps Promise types, revealing their resolved values.

type AwaitingString = Awaited<Promise<string>>;
// Result: string

type AwaitingNestedPromise = Awaited<Promise<Promise<number>>>;
// Result: number

type AwaitingMixedType = Awaited<boolean | Promise<number>>;
// Result: number | boolean

Readonly<Type>: Safeguarding Immutability

Readonly ensures that all properties of a type are immutable, preventing unintended modifications.

interface Point {
  readonly x: number;
  readonly y: number;
}

const origin: Readonly<Point> = { x: 0, y: 0 };

Record<Keys, Type>: Crafting Precise Structures

Record constructs objects with specified keys and corresponding value types.

type ContactInfo = Record<"email" | "phone", string>;

const contact: ContactInfo = { email: "example@example.com", phone: "+123456789" };

Required<Type>: Makes properties mandatory

Required constructs a type consisting of all properties of Type set to required.

interface Props {
  a?: number;
  b?: string;
}

const obj: Props = { a: 5 };

const obj2: Required<Props> = { a: 5 };
// Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.

Pick<Type, Keys>: Selecting Specific Properties

Pick extracts specific properties from a type, creating a new tailored type.

interface Car {
  make: string;
  model: string;
  year: number;
}

type CarInfo = Pick<Car, "make" | "year">;

const carDetails: CarInfo = { make: "Toyota", year: 2022 };

Omit<Type, Keys>: Excluding Unwanted Properties

Omit removes specified properties from a type, producing a refined subset.

interface Book {
  title: string;
  author: string;
  pages: number;
}

type BookPreview = Omit<Book, "pages">;

const preview: BookPreview = { title: "The Great Gatsby", author: "F. Scott Fitzgerald" };

Exclude<UnionType, ExcludedMembers>: Filtering Union Types

Exclude eliminates members from a union type based on specified exclusions.

type NonNumeric = Exclude<number | string | boolean, number>;

const values: NonNumeric[] = ["apple", true];

Extract<Type, Union>: Retrieving Union Members

Extract fetches members from a union type based on specified inclusion criteria.

type Numeric = Extract<number | string | boolean, number>;

const nums: Numeric[] = [10, 20];

NonNullable<Type>: Removes null and undefined

NonNullable constructs a type by excluding null and undefined from Type.

type T0 = NonNullable<string | number | undefined>;
// T0: string | number

Parameters<Type>: Extracts function parameters

Parameters make a tuple type from the types used in the parameters of a function type Type.

declare function f1(arg: { a: number; b: string }): void;

type T0 = Parameters<() => string>;
// T0: []

type T1 = Parameters<(s: string) => void>;
// T1: [s: string]

type T2 = Parameters<<T>(arg: T) => T>;
// T2: [arg: unknown]

type T3 = Parameters<typeof f1>;
// T3: [arg: { a: number; b: string }]

ConstructorParameters<Type>: Extracts constructor parameters

ConstructorParameters make a tuple or array type from the types of a constructor function type.

type T0 = ConstructorParameters<ErrorConstructor>;
// T0: [message?: string]

type T1 = ConstructorParameters<FunctionConstructor>;
// T1: string[]

type T2 = ConstructorParameters<RegExpConstructor>;
// T2: [pattern: string | RegExp, flags?: string]

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

type T3 = ConstructorParameters<typeof C>;
// T3: [a: number, b: string]

ReturnType<Type>: Extracts return type

ReturnType constructs a type consisting of the return type of function Type.

declare function f1(): { a: number; b: string };

type T0 = ReturnType<() => string>;
// T0: string

type T1 = ReturnType<(s: string) => void>;
// T1: void

type T2 = ReturnType<typeof f1>;
// T2: { a: number; b: string }

InstanceType<Type>: Returns instance type

InstanceType constructs a type consisting of the instance type of a constructor function in Type.

class C {
  x = 0;
  y = 0;
}

type T0 = InstanceType<typeof C>;
// T0: C

ThisParameterType<Type>: Extracts 'this' type

ThisParameterType extracts the type of the this parameter for a function type.

function toHex(this: Number) {
  return this.toString(16);
}

function numberToString(n: ThisParameterType<typeof toHex>) {
  return toHex.apply(n);
}

OmitThisParameter<Type>: Removes 'this' parameter

OmitThisParameter removes the this parameter from Type.

function toHex(this: Number) {
  return this.toString(16);
}

const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);
console.log(fiveToHex());

ThisType<Type>: Contextual 'this' marker

ThisType serves as a marker for a contextual this type.

type ObjectDescriptor<D, M> = {
  data?: D;
  methods?: M & ThisType<D & M>; // Type of 'this' in methods is D & M
};

function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
  let data: object = desc.data || {};
  let methods: object = desc.methods || {};
  return { ...data, ...methods } as D & M;
}

Silicon valley GIF - Find on GIFER

These examples only scratch the surface of TypeScript utility types. Embrace their versatility, leverage their capabilities, and witness your TypeScript endeavours soar to new heights of clarity and efficiency.

Once you go TypeScript, you never go back ;)

Thank you for reading the article! If you found it helpful, consider giving me a follow on GitHub for more insightful content and updates.

Β