More Utility Types

Beyond reshaping objects, TypeScript's built-in utilities can filter unions, strip out null, and pull types out of functions, classes and promises. These are the tools for deriving types from code you already have instead of writing them twice.

Exclude and Extract

Both work on union types. Exclude<U, X> removes the members of U that are assignable to X; Extract<U, X> keeps only those members:

TS exclude-extract.ts
type Status = "idle" | "loading" | "success" | "error";

type Busy = Extract<Status, "loading">;
type Settled = Exclude<Status, "idle" | "loading">;

type Mixed = string | number | (() => void);
type Callable = Extract<Mixed, Function>;
type Data = Exclude<Mixed, Function>;

const s: Settled = "error";
const d: Data = 42;
console.log(s, d);
Output
error 42

Settled is "success" | "error", and Data is string | number. Compare with Omit, which removes properties from an object type; Exclude removes members from a union.

NonNullable

NonNullable<T> removes null and undefined from a type:

TS non-nullable.ts
type MaybeName = string | null | undefined;
type Name = NonNullable<MaybeName>;

function shout(n: Name) {
  return n.toUpperCase();
}

console.log(shout("ok"));
Output
OK

ReturnType and Parameters

These extract the types from a function's signature. They're invaluable when a library exports a function but not the types it uses:

TS return-params.ts
function createSession(userId: number, opts: { ttl: number; remember: boolean }) {
  return { token: `t-${userId}`, expiresIn: opts.ttl, remember: opts.remember };
}

type Session = ReturnType<typeof createSession>;
type SessionArgs = Parameters<typeof createSession>;
type SessionOpts = Parameters<typeof createSession>[1];

const opts: SessionOpts = { ttl: 3600, remember: true };
const args: SessionArgs = [7, opts];
const session: Session = createSession(...args);
console.log(session);
Output
{ token: 't-7', expiresIn: 3600, remember: true }

Parameters returns a tuple of the parameter types, so [1] picks the second one. Note the typeof: these utilities take a function type, and typeof createSession turns the function value into its type.

Awaited

Awaited<T> unwraps the value type of a promise, the same way await does — including nested promises:

TS awaited.ts
async function loadUser() {
  return { id: 1, name: "Ana" };
}

type UserPromise = ReturnType<typeof loadUser>;
type User = Awaited<UserPromise>;

const cached: User = { id: 2, name: "Bo" };
loadUser().then(u => console.log(u.name, cached.name));
Output
Ana Bo

UserPromise is Promise<{ id: number; name: string }>, and Awaited gives you the object inside. Awaited<ReturnType<typeof fn>> is a very common combination for async functions.

Classes: InstanceType and ConstructorParameters

TS class-utils.ts
class Connection {
  constructor(public host: string, public port: number) {}
}

type ConnArgs = ConstructorParameters<typeof Connection>;
type Conn = InstanceType<typeof Connection>;

function connectAll(list: ConnArgs[]): Conn[] {
  return list.map(args => new Connection(...args));
}

console.log(connectAll([["db.local", 5432]])[0].port);
Output
5432

For a class you declared yourself, InstanceType<typeof Connection> is the same as just writing Connection. It's mainly useful in generic code that receives a class as a value.

String-case utilities

Four intrinsic types transform string literal types: Uppercase, Lowercase, Capitalize and Uncapitalize. They're mostly used with template literal types (lesson 30):

TS string-utils.ts
type Event = "click" | "focus";
type Handler = `on${Capitalize<Event>}`;

const handlers: Record<Handler, () => void> = {
  onClick: () => console.log("clicked"),
  onFocus: () => console.log("focused"),
};

handlers.onClick();
Output
clicked

NoInfer

Added in TypeScript 5.4, NoInfer<T> tells the compiler not to use a particular argument when inferring a type parameter:

TS no-infer.ts
function pick<T extends string>(options: T[], fallback: NoInfer<T>): T {
  return options[0] ?? fallback;
}

pick(["small", "large"], "small");
pick(["small", "large"], "medium");
tsc output
error TS2345: Argument of type '"medium"' is not assignable to parameter of type '"small" | "large"'.

Without NoInfer, TypeScript would infer T from both arguments, widen it to include "medium", and accept the typo.

Quick reference

UtilityWorks onResult
Exclude<U, X>UnionsMembers of U not assignable to X
Extract<U, X>UnionsMembers of U assignable to X
NonNullable<T>AnyT without null/undefined
ReturnType<F>Function typesReturn type
Parameters<F>Function typesTuple of parameter types
Awaited<T>PromisesResolved value type
InstanceType<C>Class typesInstance type
ConstructorParameters<C>Class typesTuple of constructor parameter types
NoInfer<T>Type parametersBlocks inference from that position

Try it yourself

Try it yourself
Console output