TypeScriptadvanced
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.
TypeScriptintermediate
type Assignment = { kind: "assign"; key: string; value: string };
type Flag = { kind: "flag"; key: string };
type Token = Assignment | Flag | { kind: "comment"; text: string };
const tokenize = (line: string): Token => {
Use utility conditional types to parse and narrow a small input grammar.
TypeScriptadvanced
interface LoggerPlugin { kind: "logger"; log(message: string): void }
interface CachePlugin { kind: "cache"; get(key: string): unknown }
export type AppPlugin = LoggerPlugin | CachePlugin;
Narrow a plugin union by checking which method it has, instead of switching on a kind tag, so adding a capability-only plugin needs no tag.
TypeScriptadvanced
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.
TypeScriptintermediate
export const Status = { Pending: "pending", Active: "active", Done: "done" } as const;
export type Status = (typeof Status)[keyof typeof Status];
export const describe = (status: Status): string => {
Get enum-like values and a matching type from one object literal, without the runtime overhead or the awkward reverse-mapping of enum.
TypeScriptadvanced
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.
TypeScriptintermediate
type Nested = Promise<Promise<Promise<string>>>;
type Flat = Awaited<Nested>;
export async function loadUser(): Promise<{ id: string; name: string }> {
return { id: "1", name: "Ada" };
Unwrap Promise<Promise<Promise<T>>> down to T in one step, the way await does at runtime, using TypeScript's built-in recursive helper.
TypeScriptintermediate
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
const isJsonValue = (value: unknown): value is JsonValue => {
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return true;
if (Array.isArray(value)) return value.every(isJsonValue);
Define exactly what JSON.stringify can round-trip, recursively, and use it to reject values like undefined, functions, and Dates.
TypeScriptintermediate
export function assertIsString(value: unknown, name: string): asserts value is string {
if (typeof value !== "string") {
throw new TypeError(`${name} must be a string, got ${typeof value}`);
}
}
Narrow a value by throwing instead of branching, so the rest of the function can assume the checked type without an extra if.
TypeScriptintermediate
type ApiUser = { id: string; email: string };
const isApiUser = (value: unknown): value is ApiUser =>
typeof value === "object" &&
value !== null &&
Accept untyped input like JSON.parse's output without any, by narrowing it through a type predicate before touching its fields.
TypeScriptadvanced
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
export type DeepPartial<T> = T extends readonly unknown[]
? T
: T extends object
? { [K in keyof T]?: DeepPartial<T[K]> }
: T;
Make every property optional at every nesting level, for patch-style updates, without touching arrays element by element.
TypeScriptadvanced
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.
TypeScriptintermediate
type Brand<T, Name extends string> = T & { readonly __brand: Name };
export type UserId = Brand<string, "UserId">;
export type OrderId = Brand<string, "OrderId">;
Give two string IDs distinct types so passing one where the other belongs is a compile error, at zero runtime cost.
TypeScriptadvanced
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.
TypeScriptadvanced
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
export const toGetters = <T extends object>(obj: T): Getters<T> => {
Turn a plain object type into a matching set of getter methods, renaming every key to getX without writing each one by hand.
TypeScriptadvanced
type ParamNames<Path extends string> = Path extends `${string}:${infer Param}/${infer Rest}`
? Param | ParamNames<Rest>
: Path extends `${string}:${infer Param}`
? Param
: never;
Extract :param names out of a route string at the type level, so a route's caller must supply exactly the params that route needs.
TypeScriptintermediate
type RGB = readonly [red: number, green: number, blue: number];
const palette = {
primary: [15, 98, 254],
danger: [220, 38, 38],
Check an object against a type without widening it to that type, so autocomplete and literal inference on the value survive the check.
TypeScriptintermediate
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number }
| { kind: "rectangle"; width: number; height: number };
Make adding a new union case a compile error, not a silent runtime bug, using a switch that assigns the leftover case to never.