typescript-type-safety

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.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "typescript-type-safety" with this command: npx skills add marcioaltoe/claude-craftkit/marcioaltoe-claude-craftkit-typescript-type-safety

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

// ✅ 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

// ❌ 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" ); }

  1. Use Proper Type Guards

// ✅ 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

// ✅ 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

// ✅ 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

// ✅ 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

// ✅ 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

// ✅ 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

// ✅ 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

type NonNullable<T> = T extends null | undefined ? never : T;

type MaybeString = string | null | undefined; type DefiniteString = NonNullable<MaybeString>; // string

Extract and Exclude

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

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"], };

Type Narrowing

typeof Guards

function process(value: string | number): string { if (typeof value === "string") { return value.toUpperCase(); // value is string here } return value.toFixed(2); // value is number here }

instanceof Guards

class User { constructor(public name: string) {} }

class Admin extends User { constructor(name: string, public level: number) { super(name); } }

function greet(user: User | Admin): string { if (user instanceof Admin) { return Hello Admin ${user.name}, level ${user.level}; } return Hello ${user.name}; }

in Operator

type Dog = { bark: () => void }; type Cat = { meow: () => void };

function makeSound(animal: Dog | Cat): void { if ("bark" in animal) { animal.bark(); // animal is Dog here } else { animal.meow(); // animal is Cat here } }

TypeScript Configuration

{ "compilerOptions": { // Strict type checking "strict": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true,

// Module resolution
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,

// Interop
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,

// Other
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,

// Bun-specific
"types": ["bun-types"],
"target": "ES2022",
"lib": ["ES2022"]

} }

Best Practices

Do:

  • ✅ Use unknown instead of any

  • ✅ Implement type guards for runtime checks

  • ✅ Leverage discriminated unions for polymorphism

  • ✅ Enable strict mode in tsconfig.json

  • ✅ Use const assertions for literal types

  • ✅ Implement exhaustiveness checks in switch statements

Don't:

  • ❌ Use any type

  • ❌ Use type assertions (as) without validation

  • ❌ Disable strict mode

  • ❌ Use @ts-ignore or @ts-expect-error without good reason

  • ❌ Mix types and interfaces unnecessarily

  • ❌ Create overly complex type gymnastics

Common Type Errors and Solutions

Error: Object is possibly 'null' or 'undefined'

// ❌ Error function getName(user: User | null): string { return user.name; // Error: Object is possibly 'null' }

// ✅ Solution: Check for null function getName(user: User | null): string { if (!user) { throw new Error("User is null"); } return user.name; // OK }

// ✅ Or use optional chaining function getName(user: User | null): string | undefined { return user?.name; }

Error: Type 'X' is not assignable to type 'Y'

// ❌ Error interface User { id: string; name: string; }

const user = { id: "1", name: "John", extra: "field", };

const typedUser: User = user; // OK (structural typing)

// ✅ Use type annotation or assertion when needed const exactUser: User = { id: "1", name: "John", // extra: 'field', // Error if uncommented };

Remember

  • Type safety catches bugs at compile time - Invest in good types

  • unknown > any - Always use unknown for truly unknown types

  • Type guards are your friends - Use them liberally

  • Strict mode is mandatory - Never disable it

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

code-standards

No summary provided by upstream source.

Repository SourceNeeds Review
General

ui-designer

No summary provided by upstream source.

Repository SourceNeeds Review
General

clean-architecture

No summary provided by upstream source.

Repository SourceNeeds Review
Security

architecture-auditor

No summary provided by upstream source.

Repository SourceNeeds Review