TypeScript by Example

Filtering Unions with never

Returning never from a conditional type to remove members from a union.

The other major use of never is filtering. In a conditional type, returning never removes that branch from the resulting union.

Conditional types distribute over unions. Each union member is checked one at a time, and never disappears from the final union.

type OnlyStrings<T> = T extends string ? T : never;
 
type Mixed = string | number | boolean | "ready";
type Strings = OnlyStrings<Mixed>;
// string | "ready"
 
type WithoutDeleted<T> = T extends "deleted" ? never : T;
type Status = "draft" | "published" | "deleted";
type VisibleStatus = WithoutDeleted<Status>;
// "draft" | "published"

The built-in Exclude<T, U> uses this trick: keep members that do not extend U, and return never for the ones to remove.

type MyExclude<T, U> = T extends U ? never : T;
 
type AppEvent =
  | { kind: "user.created"; userId: string }
  | { kind: "order.paid"; orderId: string }
  | { kind: "debug"; message: string };
 
type PublicEvent = MyExclude<AppEvent, { kind: "debug" }>;

In production

Use Exclude first; write your own conditional filter when the rule is tied to your domain. Returning never is how TypeScript says "this branch should not survive the union."

Enjoyed this? Get more software craft delivered to your inbox.