10LOC

#generics

TypeScriptadvanced

Use NoInfer to keep generic inference predictable

export const createMethodGuard = <M extends string>(allowed: M[], fallback: NoInfer<M>) => {
  const allowedSet = new Set<string>(allowed);
  return (method: string): M => (allowedSet.has(method) ? (method as M) : fallback);
};

Prevent TypeScript from widening generic arguments when you need stable inference.

TypeScriptadvanced

A fluent builder pattern typed with generics

export class QueryBuilder<Shape extends Record<string, unknown> = Record<string, never>> {
  private constructor(private readonly clauses: Shape) {}

  static create(): QueryBuilder<Record<string, never>> {
    return new QueryBuilder({});

Make each .where() call widen the builder's tracked return type by exactly the key and value just added, so build() is never underspecified.

TypeScriptadvanced

Function overloads vs one generic signature — when each wins

export function parseValue(input: string): string;
export function parseValue(input: number): number;
export function parseValue(input: string | number): string | number {
  return typeof input === "string" ? input.trim() : Math.round(input);
}

Same function, two type-safe shapes: distinct return types per input type via overloads, versus one T-preserving signature via a generic.

TypeScriptadvanced

A type-safe event emitter keyed by an event-name-to-payload map

type EventMap = Record<string, unknown>;

export class TypedEmitter<Events extends EventMap> {
  private listeners: { [K in keyof Events]?: Array<(payload: Events[K]) => void> } = {};

Make on() and emit() reject mismatched event names and payloads at compile time, from one map type instead of per-event overloads.

TypeScriptadvanced

const type parameters to preserve literal inference in generics

const asConstTuple = <const T extends readonly string[]>(...values: T): T => values;

export const statuses = asConstTuple("pending", "active", "done");

export type Status = (typeof statuses)[number];

Stop a generic function from widening ['pending','active'] to string[] on the way in, without forcing every caller to write as const.

TypeScriptadvanced

Conditional types + infer to extract a function's return type

type AsyncReturnType<Fn extends (...args: never[]) => unknown> = Fn extends (
  ...args: never[]
) => Promise<infer R>
  ? R
  : Fn extends (...args: never[]) => infer R

Build a return-type extractor that also unwraps the Promise, so async and sync functions land on the same resolved type.