Type Narrowing Helper
Type a TypeScript union — string | number | null | User — and get the idiomatic narrowing code for every member: typeof for primitives, Array.isArray for arrays, instanceof for classes, the in operator for object shapes, discriminant switches for tagged unions, and custom is-guards for interfaces. It assembles a complete narrow() ladder, detects discriminated unions and emits an exhaustive switch with a never check, and ships a browsable catalogue of every technique with a when-to-use note.
About this ToolHow it works, benefits & use casesTap to collapse
Paste a TypeScript union type and the helper writes the narrowing code for every member using the right technique for each one. typeof handles primitives, Array.isArray handles arrays (typeof reports them as "object"), instanceof handles built-in and class instances, the in operator distinguishes object shapes, and named interfaces fall back to a custom is<Type>() type-guard skeleton. It assembles a complete narrow() function with an if/else-if ladder ordered from most specific to least, and when every member is an object literal sharing a literal tag like kind or status it recognises a discriminated union and adds an exhaustive switch with a never assertion in the default case. A live member breakdown shows the technique chosen for each member and why, and a browsable catalogue documents all eight techniques (including assertion functions) with a when-to-use note and a runnable example. Everything updates instantly as you type and the configuration lives in a shareable URL.
How to Use
- 1Type the union you want to narrow into the input, for example string | number | null | User, or load one of the examples.
- 2Read the generated narrow() ladder in the sticky output and the member breakdown listing the technique chosen for each member.
- 3Set the variable name and toggle the options: wrap in a narrow() function, inline explanatory comments, and the exhaustive switch.
- 4For a tagged union (members sharing a literal kind/status/type key) the tool detects the discriminant and emits a switch with a never check.
- 5Open the Pattern reference dropdown to study any technique, then copy the code or share the URL.
Key Benefits
- Classifies every union member to the idiomatic narrowing technique automatically
- typeof for primitives, Array.isArray for arrays, instanceof for classes, in for object shapes, custom guards for interfaces
- Detects discriminated unions and emits an exhaustive switch with a compile-time never assertion
- Generates a complete narrow() function with a correctly ordered if/else-if ladder
- Live member breakdown explains which technique was chosen and why
- Catalogue of eight techniques (including assertion functions) with when-to-use notes and examples
- Instant live preview and a shareable URL capturing the union and options
Common Use Cases
- Scaffolding a switch over a discriminated-union state machine (e.g. a fetch result with ok/error variants)
- Writing a custom type guard to validate an unknown API response before using it
- Narrowing a string | string[] | Date | User union where each member needs a different check
- Learning why typeof fails on arrays and when to reach for instanceof versus the in operator
- Generating an exhaustive handler so adding a new union variant becomes a compile error until handled
4 members narrowed
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
// TODO: assert the properties that make a value a User
true
);
}
function narrow(value: string | string[] | Date | User) {
if (typeof value === 'string') {
// value is narrowed to string
return value.toUpperCase();
} else if (Array.isArray(value)) {
// value is narrowed to string[]
return value.length;
} else if (value instanceof Date) {
// value is narrowed to Date
return value;
} else if (isUser(value)) {
// value is narrowed to User
return value;
}
}stringtypeoftypeof narrows primitives. typeof x === 'string'.string[]Array.isArrayArrays are objects, so typeof is useless here — use Array.isArray().DateinstanceofDate is a built-in constructor — narrow with instanceof.Usercustom guardUser is a named type — a custom isUser() guard works whether it is a class or interface.
Pattern reference
Primitive members: string, number, boolean, symbol, bigint.
if (typeof value === 'string') {
value.toUpperCase(); // value: string
}Union type
Separate members with |. Object literals like { kind: 'circle'; radius: number } that share a literal tag are detected as a discriminated union.
Generation options
adds the typed signature
explain each branch
discriminated unions: never check
One technique does not fit every member
Narrowing a union means picking the right runtime check for each member. typeof only works for primitives; arrays need Array.isArray because typeof reports them as "object"; classes use instanceof; interfaces have no runtime presence so they need a custom is<Type> guard. This tool classifies every member for you and assembles the matching ladder.
Exhaustive by construction
When every member is an object literal sharing a literal tag (kind, status, type), the tool emits a switch with a never assertion in the default case — so adding a new variant later becomes a compile-time error until you handle it.
Was this tool helpful?
Share Your Experience
Help others discover this tool!
Related tools
- TypeScript FormatterFormat and beautify TypeScript code
- JSON to TypeScriptGenerate TypeScript interfaces from JSON
- Discriminated Union GeneratorBuild a tagged union and get the type, per-variant guards, an exhaustive switch, factories and a match() helper — live
- Interface MergerMerge multiple interfaces (merged / intersection / extends / deep) with conflict detection and keep-first/last/union resolution
- JSON Schema ⇄ Zod ConverterConvert JSON Schema to Zod and back — with strict mode for OpenAI/Anthropic structured outputs and function calling
- Type Guard GeneratorGenerate recursive runtime type guards (and assertion functions) from a TS interface, with a live validator that tests any JSON value
It classifies each member of the union. Primitives (string, number, boolean, symbol, bigint) get typeof; null and undefined get a strict equality check; T[] and Array<T> get Array.isArray; built-in constructors like Date, Error and Map get instanceof; inline object literals get the in operator or, if they share a literal tag, a discriminant switch; and named interface references fall back to a custom is<Type>() type guard, which works whether the name is a class or an interface.
A discriminated (tagged) union is one where every member is an object literal that shares a common property typed as a literal — like kind: "circle" versus kind: "square", or status: "ok" versus status: "error". The tool detects when all members share such a key with distinct literal values, treats it as the discriminant, and generates a switch over that key instead of separate in checks.

