๐ŸŽ“ A1Learner
๐Ÿ”
0 of 37 lessons completed

TypeScript Conditional Type Patterns

Conditional patterns are small reusable builds that combine conditionals with infer and indexed access. Each build is proven by values the computed type accepts and one value it refuses.

Flatten removes one level

Flatten matches its input against an array pattern with infer. The grid type loses one level, so the flat type holds plain strings.

type Flatten<T> = T extends (infer U)[] ? U : T;
type Row = string[];
type Grid = Row[];
type Flat = Flatten<Grid>;
const flat: Flat = ['a', 'b'];
console.log(flat.join(','));

Picking the function members

FunctionKeys maps each member to its key when the member is a function and to never otherwise. Indexing the map by every key keeps only the function names.

interface Profile {
  userName: string;
  age: number;
  save(): void;
  reset(): void;
}
type FunctionKeys<T> = { [K in keyof T]: T[K] extends (...args: any[]) => unknown ? K : never }[keyof T];
type Actions = FunctionKeys<Profile>;
const action: Actions = 'save';
const second: Actions = 'reset';
console.log(action);
console.log(second);

A dropped member name is refused

The age member holds a number, so the picking build drops its name. Assigning that name to the result fails.

interface Profile {
  userName: string;
  age: number;
  save(): void;
  reset(): void;
}
type FunctionKeys<T> = { [K in keyof T]: T[K] extends (...args: any[]) => unknown ? K : never }[keyof T];
type Actions = FunctionKeys<Profile>;
const action: Actions = 'age';
console.log(action);

Example

interface Editor {
  titleText: string;
  wordCount: number;
  publish(): void;
  archive(): void;
}
type Flatten<T> = T extends (infer U)[] ? U : T;
type FunctionKeys<T> = { [K in keyof T]: T[K] extends (...args: any[]) => unknown ? K : never }[keyof T];
type Batch = Flatten<Editor[][]>;
type Actions = FunctionKeys<Editor>;
const docs: Batch = [{ titleText: 'Draft', wordCount: 10, publish() {}, archive() {} }];
const action: Actions = 'publish';
console.log(docs[0].titleText);
console.log(action);

Key points

  • A pattern build states its steps as separate aliases, so each step can be checked on its own.
  • Flatten reads the element type out of an array type, which removes one level of nesting.
  • A member-picking build keeps the keys whose value types match a pattern and drops the rest.
  • IsAny<T> checks 0 extends (1 & T), a test that holds only when T is any.
  • IsNever<T> checks [T] extends [never], a test that holds only when T is never.

In this example, Flatten turns a grid of editors into a flat editor array, and FunctionKeys picks the action names out of the same editor shape.

Try it Yourself

Edit the code, then press Run.

Running is simulated ยท TypeScript Playground

Exercise

What does Flatten<string[][]> produce with the Flatten build from this lesson?