typescript-type-safety
TypeScript type safety including type guards and advanced type system features. **ALWAYS use when writing ANY TypeScript code (frontend AND backend)** to ensure strict type safety, avoid `any` types, and leverage the type system. Examples - "create function", "implement class", "define interface", "type guard", "discriminated union", "type narrowing", "conditional types", "handle unknown types".
What this skill does
You are an expert in TypeScript's type system and type safety. You guide developers to write type-safe code that leverages TypeScript's powerful type system to catch errors at compile time.
**For development workflow and quality gates (pre-commit checklist, bun commands), see `project-workflow` skill**
## When to Engage
You should proactively assist when:
- Working with `unknown` types in any context
- Implementing type guards for context-specific types
- Using discriminated unions within bounded contexts
- Implementing advanced TypeScript patterns without over-abstraction
- User asks about type safety or TypeScript features
## Modular Monolith Type Safety
### Context-Specific Types
```typescript
// ✅ GOOD: Each context owns its types
// contexts/auth/domain/types/user.types.ts
export interface AuthUser {
id: string;
email: string;
isActive: boolean;
}
// contexts/tax/domain/types/calculation.types.ts
export interface TaxCalculation {
ncmCode: string;
rate: number;
amount: number;
}
// ❌ BAD: Shared generic types that couple contexts
// shared/types/base.types.ts
export interface BaseEntity<T> {
// NO! Creates coupling
id: string;
data: T;
}
```
## Core Type Safety Rules
### 1. NEVER Use `any`
```typescript
// ❌ FORBIDDEN - Disables type checking
function process(data: any) {
return data.value; // No type safety at all
}
// ✅ CORRECT - Use unknown with type guards
function process(data: unknown): string {
if (isProcessData(data)) {
return data.value; // Type-safe access
}
throw new TypeError("Invalid data structure");
}
interface ProcessData {
value: string;
}
function isProcessData(data: unknown): data is ProcessData {
return (
typeof data === "object" &&
data !== null &&
"value" in data &&
typeof (data as ProcessData).value === "string"
);
}
```
### 2. Use Proper Type Guards
```typescript
// ✅ Type predicate (narrows type)
function isString(value: unknown): value is string {
return typeof value === "string";
}
function isNumber(value: unknown): value is number {
return typeof value === "number";
}
function isObject(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
// ✅ Complex type guard
interface User {
id: string;
email: string;
name: string;
}
function isUser(value: unknown): value is User {
if (!isObject(value)) return false;
return (
"id" in value &&
typeof value.id === "string" &&
"email" in value &&
typeof value.email === "string" &&
"name" in value &&
typeof value.name === "string"
);
}
// Usage
function processUser(data: unknown): User {
if (!isUser(data)) {
throw new TypeError("Invalid user data");
}
// TypeScript knows data is User here
return data;
}
```
## Discriminated Unions
```typescript
// ✅ Discriminated unions for polymorphic data
type PaymentMethod =
| {
type: "credit_card";
cardNumber: string;
expiryDate: string;
cvv: string;
}
| {
type: "paypal";
email: string;
}
| {
type: "bank_transfer";
accountNumber: string;
routingNumber: string;
};
function processPayment(method: PaymentMethod, amount: number): void {
switch (method.type) {
case "credit_card":
// TypeScript knows method has cardNumber, expiryDate, cvv
console.log(`Charging ${amount} to card ${method.cardNumber}`);
break;
case "paypal":
// TypeScript knows method has email
console.log(`Charging ${amount} to PayPal ${method.email}`);
break;
case "bank_transfer":
// TypeScript knows method has accountNumber, routingNumber
console.log(
`Charging ${amount} via bank transfer ${method.accountNumber}`
);
break;
default:
// Exhaustiveness check
const _exhaustive: never = method;
throw new Error(`Unhandled payment method: ${_exhaustive}`);
}
}
```
## Conditional Types
```typescript
// ✅ Conditional types for flexible APIs
type ResponseData<T> = T extends { id: string }
? { success: true; data: T }
: never;
type User = { id: string; name: string };
type UserResponse = ResponseData<User>; // { success: true; data: User }
// ✅ Extract promise type
type Awaited<T> = T extends Promise<infer U> ? U : T;
type UserPromise = Promise<User>;
type UserType = Awaited<UserPromise>; // User
// ✅ Extract function return type
type ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never;
function getUser(): User {
return { id: "1", name: "John" };
}
type UserFromFunction = ReturnType<typeof getUser>; // User
```
## Mapped Types
```typescript
// ✅ Make all properties optional
type Partial<T> = {
[P in keyof T]?: T[P];
};
type User = {
id: string;
email: string;
name: string;
};
type PartialUser = Partial<User>;
// { id?: string; email?: string; name?: string }
// ✅ Make all properties readonly
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type ReadonlyUser = Readonly<User>;
// ✅ Pick specific properties
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type UserPreview = Pick<User, "id" | "name">;
// { id: string; name: string }
// ✅ Omit specific properties
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type UserWithoutId = Omit<User, "id">;
// { email: string; name: string }
```
## Template Literal Types
```typescript
// ✅ Type-safe string patterns
type EventName = "user" | "order" | "payment";
type EventAction = "created" | "updated" | "deleted";
type Event = `${EventName}:${EventAction}`;
// 'user:created' | 'user:updated' | 'user:deleted' |
// 'order:created' | 'order:updated' | 'order:deleted' |
// 'payment:created' | 'payment:updated' | 'payment:deleted'
function emitEvent(event: Event): void {
console.log(`Emitting ${event}`);
}
emitEvent("user:created"); // ✅ OK
emitEvent("user:invalid"); // ❌ Type error
```
## Function Overloads
```typescript
// ✅ Function overloads for different input/output types
function getValue(key: "count"): number;
function getValue(key: "name"): string;
function getValue(key: "isActive"): boolean;
function getValue(key: string): unknown {
// Implementation
const values: Record<string, unknown> = {
count: 42,
name: "John",
isActive: true,
};
return values[key];
}
const count = getValue("count"); // Type is number
const name = getValue("name"); // Type is string
const isActive = getValue("isActive"); // Type is boolean
```
## Const Assertions
```typescript
// ✅ Const assertions for literal types
const config = {
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3,
} as const;
// config.apiUrl type is 'https://api.example.com' (literal)
// config.timeout type is 5000 (literal)
// config is readonly
// ✅ Array as const
const colors = ["red", "green", "blue"] as const;
// Type is readonly ['red', 'green', 'blue']
type Color = (typeof colors)[number];
// Type is 'red' | 'green' | 'blue'
```
## Utility Types
### NonNullable
```typescript
type NonNullable<T> = T extends null | undefined ? never : T;
type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>; // string
```
### Extract and Exclude
```typescript
type Extract<T, U> = T extends U ? T : never;
type Exclude<T, U> = T extends U ? never : T;
type Status = "pending" | "approved" | "rejected" | "cancelled";
type PositiveStatus = Extract<Status, "approved" | "pending">;
// 'approved' | 'pending'
type NegativeStatus = Exclude<Status, "approved" | "pending">;
// 'rejected' | 'cancelled'
```
### Record
```typescript
type Record<K extends keyof unknown, T> = {
[P in K]: T;
};
type UserRoles = "admin" | "user" | "guest";
type Permissions = Record<UserRoles, string[]>;
// {
// admin: string[];
// user: string[];
// guest: string[];
// }
const permissions: Permissions = {
admin: ["read", "write", "delete"],
user: ["read", "write"],
guest: ["read"]Related in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.