Teaches how to write custom type guards with type predicates and use built-in type narrowing in TypeScript. Use when working with unknown types, union types, validating external data, or implementing type-safe runtime checks.
Teaches how to write custom type guards with type predicates and use built-in type narrowing in TypeScript. Use when working with unknown types, union types, validating external data, or implementing type-safe runtime checks.
/plugin marketplace add djankies/claude-configs/plugin install typescript@claude-configsThis skill is limited to using the following tools:
references/advanced-patterns.mdreferences/nested-validation.mdreferences/testing-guide.mdunknown or any typesThree Categories:
typeof, instanceof, in, Array.isArray()value is TypeKey Pattern: Runtime check → Type narrowing → Safe access </overview>
<workflow> ## Type Guard Selection FlowStep 1: Identify the Type Challenge
What do you need to narrow?
typeofinstanceofin or custom type predicateArray.isArray() + element checksStep 2: Choose Guard Strategy
Built-in Guards (primitives, classes, simple checks)
typeof value === "string"
value instanceof Error
"property" in value
Array.isArray(value)
Custom Type Predicates (object shapes, complex logic)
function isUser(value: unknown): value is User {
return typeof value === "object" &&
value !== null &&
"id" in value;
}
Validation Libraries (nested structures, multiple fields)
const UserSchema = z.object({
id: z.string(),
email: z.string().email()
});
Step 3: Implement Guard
null and undefined firstfunction processValue(value: unknown): string {
if (typeof value === "string") {
return value.toUpperCase();
}
if (typeof value === "number") {
return value.toFixed(2);
}
if (typeof value === "boolean") {
return value ? "yes" : "no";
}
throw new Error("Unsupported type");
}
typeof Guards Work For:
"string", "number", "boolean", "symbol", "undefined""function", "bigint""object" (but also matches null - check for null first!)function handleError(error: unknown): string {
if (error instanceof Error) {
return error.message;
}
if (error instanceof CustomError) {
return `Custom error: ${error.code}`;
}
return String(error);
}
instanceof Works For:
type Circle = { kind: "circle"; radius: number };
type Square = { kind: "square"; sideLength: number };
type Shape = Circle | Square;
function getArea(shape: Shape): number {
if ("radius" in shape) {
return Math.PI * shape.radius ** 2;
} else {
return shape.sideLength ** 2;
}
}
in Guard Best For:
interface User {
id: string;
name: string;
email: string;
}
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
"name" in value &&
"email" in value &&
typeof (value as User).id === "string" &&
typeof (value as User).name === "string" &&
typeof (value as User).email === "string"
);
}
function processUser(data: unknown): void {
if (isUser(data)) {
console.log(data.name);
} else {
throw new Error("Invalid user data");
}
}
function isStringArray(value: unknown): value is string[] {
return Array.isArray(value) && value.every(item => typeof item === "string");
}
function processNames(data: unknown): string {
if (isStringArray(data)) {
return data.join(", ");
}
throw new Error("Expected array of strings");
}
For complex nested structures, compose guards from simpler guards. See references/nested-validation.md for detailed examples.
type Success = {
status: "success";
data: string;
};
type Failure = {
status: "error";
error: string;
};
type Result = Success | Failure;
function handleResult(result: Result): void {
if (result.status === "success") {
console.log("Data:", result.data);
} else {
console.error("Error:", result.error);
}
}
function isSuccess(value: unknown): value is Success {
return (
typeof value === "object" &&
value !== null &&
"status" in value &&
value.status === "success" &&
"data" in value &&
typeof (value as Success).data === "string"
);
}
function isFailure(value: unknown): value is Failure {
return (
typeof value === "object" &&
value !== null &&
"status" in value &&
value.status === "error" &&
"error" in value &&
typeof (value as Failure).error === "string"
);
}
function isResult(value: unknown): value is Result {
return isSuccess(value) || isFailure(value);
}
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== "string") {
throw new Error(`Expected string, got ${typeof value}`);
}
}
function assertIsUser(value: unknown): asserts value is User {
if (!isUser(value)) {
throw new Error("Invalid user data");
}
}
function processData(data: unknown): void {
assertIsUser(data);
console.log(data.name);
}
Assertion Functions:
asserts value is Type return typeLocal References (in references/ directory):
advanced-patterns.md - Optional properties, arrays, tuples, records, enumsnested-validation.md - Complex nested object validation patternstesting-guide.md - Complete unit testing guide with examplesRelated Skills:
null and undefined before property accessin operatorvalue is Type) for reusable guardsSHOULD:
isType or assertIsTypeNEVER:
null (typeof null === "object"!)inBasic Patterns (covered in examples above):
Advanced Patterns (see references/advanced-patterns.md):
Null Safety:
null before using in or property accessundefined for optional valuesComplete Validation:
Type Predicate:
value is Type for reusable guardsasserts value is Type for assertion functionsEdge Cases:
Composition:
Test all edge cases: valid inputs, missing properties, wrong types, null, undefined, and non-objects.
See references/testing-guide.md for complete testing strategies and examples.
</testing-guards>
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.