Automated summary
This skill explains how to narrow TypeScript union types using type guards, assertion functions, and control flow analysis.
Strengths
- Covers the main narrowing techniques: typeof, instanceof, the in operator, is predicates, asserts functions, and discriminated unions
- Provides complete, directly reusable code examples for each technique
- Clarifies the important distinction between a plain boolean return, an is predicate, and an asserts function
- Shows how to safely validate unknown values from APIs and how to properly type Array.filter results
Limitations
- The supplied content is truncated: the Details section cuts off mid-word at "Trut"
- Does not cover schema validation libraries (Zod, io-ts, valibot) or how they combine with type guards
- Omits advanced topics such as type-level tests or complex generic predicates
Use it when you need to narrow a union type, validate external data, or replace unsafe as assertions with safe runtime checks in TypeScript.
Skip it if your project is plain JavaScript or already relies on a schema validation library that handles narrowing for you.
Security analysis
SafeThe skill is a static reference document about TypeScript type guards and contains only code examples and explanations. It declares no tools and performs no execution, network, or file operations, so there is no meaningful security risk.
No concerns found
How this analysis is madeExamples
Refactor this function so it takes a Cat | Dog union and uses a custom `is` predicate type guard instead of an `as` cast, then call the right method in each branch.Write an isUser type guard that accepts `unknown` and checks id and email are strings, building on an isRecord helper, so the parsed JSON response is safely narrowed.This filter returns (string | null)[] but I want string[] — rewrite it using a type predicate in the filter callback.TypeScript Type Guards
Narrow union types safely using type guards, assertion functions, and control flow
When to Use
- Narrowing union types to access type-specific properties
- Validating unknown data from APIs or user input
- Replacing unsafe type assertions (
as) with safe runtime checks - Creating reusable narrowing functions for domain types
Instructions
typeofguard for primitives:
function format(value: string | number): string {
if (typeof value === 'string') {
return value.toUpperCase(); // Narrowed to string
}
return value.toFixed(2); // Narrowed to number
}
instanceofguard for class instances:
function handleError(error: Error | string): string {
if (error instanceof Error) {
return error.message; // Narrowed to Error
}
return error; // Narrowed to string
}
- Custom type guard with
ispredicate:
interface Cat {
meow(): void;
}
interface Dog {
bark(): void;
}
function isCat(animal: Cat | Dog): animal is Cat {
return 'meow' in animal;
}
function interact(animal: Cat | Dog) {
if (isCat(animal)) {
animal.meow(); // Narrowed to Cat
} else {
animal.bark(); // Narrowed to Dog
}
}
- Narrowing with
inoperator:
type ApiResponse = { status: 'success'; data: User } | { status: 'error'; message: string };
function handle(response: ApiResponse) {
if ('data' in response) {
console.log(response.data); // Narrowed to success variant
}
}
- Assertion function — narrows or throws:
function assertDefined<T>(value: T | null | undefined, name: string): asserts value is T {
if (value == null) {
throw new Error(`Expected ${name} to be defined`);
}
}
function process(user: User | null) {
assertDefined(user, 'user');
// user is narrowed to User after this line
console.log(user.name);
}
- Narrow
unknownvalues safely:
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function isUser(value: unknown): value is User {
return isRecord(value) && typeof value.id === 'string' && typeof value.email === 'string';
}
- Array filtering with type guards:
const items: (string | null)[] = ['a', null, 'b', null, 'c'];
// Without type guard: (string | null)[]
const filtered = items.filter((x) => x !== null);
// With type guard: string[]
const filtered = items.filter((x): x is string => x !== null);
- Discriminated union narrowing:
type Shape = { kind: 'circle'; radius: number } | { kind: 'rect'; width: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'rect':
return shape.width * shape.height;
}
}
Details
Type guards are runtime checks that TypeScript uses to narrow types in subsequent code. They bridge the gap between TypeScript's static type system and JavaScript's dynamic runtime.
Control flow analysis: TypeScript tracks type narrowing through if/else, switch, return, throw, and assignment. After a type guard condition, the type is narrowed in the true branch and the complement is narrowed in the else branch.
is predicates vs boolean returns: A function returning boolean does not narrow types at the call site. A function returning x is Type does. The is predicate is a promise to the compiler — if you return true, the value IS that type.
Assertion functions (asserts x is T) narrow the type AFTER the function call returns. If the assertion fails, the function must throw. This is useful for precondition checks at the top of functions.
asserts vs is:
is— used in conditions (if,filter). Returns boolean.asserts— used as a statement. Throws on failure. Narrows for subsequent code.
Truthiness narrowing: TypeScript narrows if (value) to exclude null, undefined, 0, '', and false. This is convenient but can exclude valid falsy values — use explicit != null when 0 or '' are valid.
Trade-offs:
- Custom type guards with
isput the burden of correctness on the developer — an incorrect guard silently misnarrows instanceofdoes not work across iframes, realms, or serialized objects- Assertion functions cannot be used in expression position — they must be standalone statements
- Deep narrowing of nested objects requires multiple guard calls or a comprehensive validation library like Zod
Source
https://typescriptlang.org/docs/handbook/2/narrowing.html
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.
From the file to a first result
The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.
View the source and supporting filesThis download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.
1. Download the file
Download SKILL.mdFrom the root of a test project, save the file as .skill-downloads/typescript-type-guards-ms1aw5bo/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.
Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.
mkdir -p '.skill-downloads/typescript-type-guards-ms1aw5bo' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/typescript-type-guards-ms1aw5bo/download' --output '.skill-downloads/typescript-type-guards-ms1aw5bo/SKILL.md.part' && mv '.skill-downloads/typescript-type-guards-ms1aw5bo/SKILL.md.part' '.skill-downloads/typescript-type-guards-ms1aw5bo/SKILL.md'2. Check prerequisites
Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.
No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.
The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.
3. Load the skill
This file does not provide a usable name/description header for this native procedure. It remains available to download and inspect; use the manual method below.
Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.
4. Try a first task
Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:
Read .skill-downloads/typescript-type-guards-ms1aw5bo/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.
React Hook Form Best Practices
Development
Master React Hook Form: avoid anti-patterns, manage subscriptions, and handle reset, dirty state, and controlled inputs correctly.
Emotion to Mantine Migration
Development
Migrate Emotion styled-components to Mantine components with style props and CSS modules. Use when converting .styled.tsx files or removing @emotion imports.
Scope Creep Detector
Development
Analyzes git diffs against a stated intent to detect scope creep, unrelated files, broad PRs, and oversized changes, then suggests keep, split, or justify actions.