From harness-claude
Narrows TypeScript union types safely using type guards, assertion functions, and control flow. Ideal for validating API data, user input, and replacing unsafe 'as' assertions.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Narrow union types safely using type guards, assertion functions, and control flow
Provides examples of advanced TypeScript patterns: generics with constraints, conditional types, mapped types, discriminated unions, and type guards. Useful for complex typing scenarios.
Guides TypeScript type system usage: generics, utility types (Partial, Pick, Omit), type narrowing, guards, discriminated unions, strict mode, and patterns with zod/valibot.
Masters TypeScript advanced types including generics, conditional types, mapped types, template literals, and utility types for type-safe applications. Use for complex type logic, reusable utilities, and compile-time safety in TS projects.
Share bugs, ideas, or general feedback.
Narrow union types safely using type guards, assertion functions, and control flow
as) with safe runtime checkstypeof guard 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
}
instanceof guard for class instances:function handleError(error: Error | string): string {
if (error instanceof Error) {
return error.message; // Narrowed to Error
}
return error; // Narrowed to string
}
is predicate: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
}
}
in operator: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
}
}
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);
}
unknown values 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';
}
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);
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;
}
}
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:
is put the burden of correctness on the developer — an incorrect guard silently misnarrowsinstanceof does not work across iframes, realms, or serialized objectshttps://typescriptlang.org/docs/handbook/2/narrowing.html