TypeScript Survival Guide

Favorite typescript videos

121 — Utility Types, Blazing Fast (Object Shapers, Union Operators, Extractors)

Full companion narrative for Matt Pocock's TypeScript Utility Types Blazing Fast — the global no-import helpers in three families plus a composed ReturnType and Awaited challenge

Video: 121 · TypeScript’s Utility Types, Blazing Fast · watch · ~03:08 · Matt Pocock

Source: this is a written companion to the video. Part of the Favorite TypeScript Videos curated path, module 03 (advanced types) — the helper catalog for the whole path: the object shapers carve the price map from 120, the union operators shrink the constrained unions from 119, NonNullable is the type-level twin of the ?. narrowing in 118, and Partial meets its strictest interaction in 114.

Matt speed-runs one file, ideas/utility-types/src/index.ts, straight through — each helper gets one typed line and its hover result, then he moves on. His opener sets the terms: “The utility types are a set of types that are available globally in all TypeScript projects. So you don’t need to add any dependencies to get this going.” Every utility type, he adds, is just available in the project without needing to import anything. Frame one freezes the object section (Omit<User, "id">), frame two the discriminated-union setup (RoleAttributes), frame three the async tail (Promise<string>).

object utilities over the User type

object shapers: Partial, Required, Omit, Pick, Readonly

Partial<User> makes every property optional — the update-payload shape where any subset of fields is legal — while Required drives optional properties back to required where a downstream step must see the complete object. Omit<User, "id"> returns the object minus a property, passing a union removes several at once, and Pick is the reverse that keeps only the named properties. They compose: Partial<Pick<User, "id" | "name">> yields id and name both optional. Readonly<User> freezes assignment, and its missing inverse is a hand-written mapped type that strips readonly — the first hint that utility types are themselves composable type-level programs. Note the strictness interaction: every property Partial makes optional becomes absent-or-present-never-undefined under the exactOptionalPropertyTypes flag from 114.

type User = { id: string; name: string; age: number };

type UserUpdate = Partial<User>; // all props optional
const patch: UserUpdate = { name: "k" }; // ok: subset legal

type NoId = Omit<User, "id">; // { name: string; age: number }
type JustIdName = Partial<Pick<User, "id" | "name">>; // both optional

type Frozen = Readonly<User>; // id, name, age all read-only
type Mutable<T> = { -readonly [K in keyof T]: T[K] }; // hand-rolled inverse

union operators: Exclude and Extract on discriminated unions

On the RoleAttributes discriminated union — where only the admin member carries orgId — Exclude drops members and accepts a union to drop several, while Extract pulls out a single member by its discriminator so its extra properties become examinable. These are the operators that shrink or select within constrained unions like the <TItem extends Animal | Human> parameter from 119 before such unions reach a signature, and they operate on the object shape that 120 ends with — an as const price map is directly Pick-able where a numeric enum is not.

type RoleAttributes = { role: "admin"; orgId: string } | { role: "user" } | { role: "anonymous" };

type NonAdmin = Exclude<RoleAttributes, { role: "admin" }>; // user | anonymous
type AdminOnly = Extract<RoleAttributes, { role: "admin" }>; // { role: "admin"; orgId: string }

RoleAttributes discriminated union for Exclude and Extract

function and async extractors: ReturnType, Parameters, NonNullable, Awaited

For funk(a: number, b: string): number, ReturnType<typeof funk> tracks the return type through later edits and Parameters<typeof funk> yields the named tuple [a: number, b: string] — the fix when an external library never exported those types. NonNullable<string | null | undefined> strips the nil members, the type-level twin of the ?. guard at the use site in 118. Promise<string> represents the async value and Awaited unwraps it. The closing challenge composes all three families: Awaited<ReturnType<typeof asyncFunk>> extracts the resolved value out of an async function’s promise.

declare function funk(a: number, b: string): number;
type R = ReturnType<typeof funk>; // number, tracks edits
type P = Parameters<typeof funk>; // [a: number, b: string], tracks added params

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

declare function asyncFunk(): Promise<number>;
type Resolved = Awaited<ReturnType<typeof asyncFunk>>; // number
const n: Resolved = 42; // ok: unwrapped, not Promise<number>

Promise and Awaited closing with the async challenge

The helper catalog the path builds on: object shapers carve shapes like the price map from 120, union operators select within constraints like the one in 119, and the same object-shaping family reappears as the scraper’s Record<string, unknown> accumulator in 123. Watch the Partial interplay with 114’s exactOptionalPropertyTypes before shipping update payloads.

Prev: 120 — TypeScript Enums Explained · Next: 122 — Design Patterns Intro

Transcript notes: type and helper names verified against the editor frames; screenshots sample one frame per family, timestamps ~00:00 (objects) through ~02:00 (async) across the ~03:08 runtime.