typescript-writing-code

TypeScript Writing Code

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-writing-code" with this command: npx skills add ivantorresedge/molcajete.ai/ivantorresedge-molcajete-ai-typescript-writing-code

TypeScript Writing Code

Quick reference for writing production-quality TypeScript code. Each section summarizes the key rules — reference files provide full examples and edge cases.

Strict TypeScript Configuration

This project enforces maximum type safety through tsconfig.base.json . Zero any tolerance — no exceptions.

Key Flags

  • strict: true — Enables all strict type-checking options as a group.

  • noImplicitAny: true — Every value must have an explicit or inferable type. No implicit any .

  • strictNullChecks: true — null and undefined are distinct types. Must be handled explicitly.

  • noUncheckedIndexedAccess: true — Array/object index access returns T | undefined . Always check before using.

  • noUnusedLocals: true — Unused variables are compile errors, not warnings.

  • noUnusedParameters: true — Unused function parameters are compile errors.

  • noImplicitReturns: true — Every code path in a function must return a value.

  • isolatedModules: true — Required for Vite/esbuild compatibility. Prevents features that need full-program analysis.

Zero any Policy

// ❌ Wrong — using any function parse(data: any): User { return data as User; }

// ✅ Correct — using unknown with narrowing function parse(data: unknown): User { if (!isUser(data)) { throw new Error("Invalid user data"); } return data; }

Safe Indexed Access

With noUncheckedIndexedAccess , array and record access returns T | undefined :

const items = ["a", "b", "c"]; const first = items[0]; // string | undefined — must check

if (first !== undefined) { console.log(first.toUpperCase()); // safe }

const map: Record<string, number> = { a: 1 }; const value = map["b"]; // number | undefined — must check

Catch Blocks

Always type catch variables as unknown :

try { await fetchData(); } catch (error: unknown) { if (error instanceof Error) { console.error(error.message); } throw error; }

See references/strict-config.md for the full tsconfig.base.json, flag explanations, and anti-patterns.

Type Safety Patterns

Use TypeScript's type system to catch bugs at compile time, not runtime.

Type Guards

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

// Custom type guard with is predicate function isUser(value: unknown): value is User { return ( typeof value === "object" && value !== null && "id" in value && "email" in value ); }

Discriminated Unions

Tag union members with a literal type field. Use exhaustive switch for safety:

type Result<T> = | { kind: "success"; data: T } | { kind: "error"; error: string };

function handle<T>(result: Result<T>): void { switch (result.kind) { case "success": console.log(result.data); break; case "error": console.error(result.error); break; default: { const _exhaustive: never = result; throw new Error(Unhandled case: ${_exhaustive}); } } }

Branded Types

Use branded types for nominal typing when primitive types are too loose:

type UserId = string & { readonly __brand: "UserId" }; type OrderId = string & { readonly __brand: "OrderId" };

function createUserId(id: string): UserId { return id as UserId; }

function getUser(id: UserId): User { /* ... */ }

// getUser(orderId) — compile error, even though both are strings

See references/type-safety.md for generics, utility types, assertion functions, and anti-patterns.

ESM Module Patterns

This project uses ESM ("type": "module" ) throughout. All packages set "type": "module" in package.json .

Import Rules

  • Named exports preferred — Default exports make refactoring harder and tree-shaking less predictable.

  • import type for types — Biome enforces useImportType and useExportType . Type-only imports are erased at runtime.

  • node: prefix for built-ins — Always use node:path , node:fs , node:crypto .

// ✅ Correct import { useState, useEffect } from "react"; import type { ReactNode } from "react"; import path from "node:path";

// ❌ Wrong — missing type keyword import { ReactNode } from "react"; // Biome error: useImportType

Barrel Files

Use barrel files (index.ts ) for clean public APIs, but keep them thin:

// components/index.ts export { Button } from "./Button"; export { Input } from "./Input"; export type { ButtonProps, InputProps } from "./types";

Dynamic Imports

Use dynamic import() for code splitting in routes and heavy modules:

const AdminPanel = lazy(() => import("./pages/AdminPanel"));

See references/esm-modules.md for build output, circular dependency detection, and package.json exports.

Error Handling

Handle errors explicitly. Use result types for expected failures, exceptions for unexpected ones.

Result Type Pattern

type Result<T, E = string> = | { ok: true; data: T } | { ok: false; error: E };

function createSuccess<T>(data: T): Result<T, never> { return { ok: true, data }; }

function createError<E>(error: E): Result<never, E> { return { ok: false, error }; }

Discriminated Union Errors

type ApiError = | { kind: "not_found"; resource: string } | { kind: "validation"; fields: Record<string, string> } | { kind: "unauthorized" };

function handleApiError(error: ApiError): string { switch (error.kind) { case "not_found": return ${error.resource} not found; case "validation": return Object.values(error.fields).join(", "); case "unauthorized": return "Please sign in"; } }

Try-Catch Rules

// ✅ Correct — catch unknown, narrow, add context try { await api.createUser(data); } catch (error: unknown) { if (error instanceof ApiError) { throw new Error(Failed to create user: ${error.message}); } throw error; // Re-throw unexpected errors }

// ❌ Wrong — catch any, swallow error try { await api.createUser(data); } catch (e: any) { console.log(e.message); // unsafe access }

See references/error-handling.md for async patterns, retry logic, and when to throw vs return errors.

Biome (Linter & Formatter)

This project uses Biome (v2.3.11) for both linting and formatting. It replaces ESLint and Prettier entirely.

Key Rules

Rule Level Effect

noExplicitAny

error Cannot use any type anywhere

noUnusedVariables

error All variables must be used

noUnusedImports

error All imports must be used

useConst

error Use const when variable is never reassigned

useImportType

error Use import type for type-only imports

useExportType

error Use export type for type-only exports

noNonNullAssertion

warn Avoid ! postfix operator

a11y recommended on Accessibility rules for JSX

Formatter Settings

  • Double quotes, semicolons always, trailing commas

  • 2-space indent, 100-character line width

  • Organize imports automatically

Commands

Check (lint + format check)

biome check .

Fix (lint fix + format)

biome check --write .

CI mode (fails on any issue)

biome ci .

Critical Rule

Never add biome-ignore comments. Fix the underlying issue instead of suppressing it. This is a hard project rule — no exceptions.

See references/biome.md for the full biome.json config, VS Code integration, and common fix patterns.

Naming Conventions & Code Quality

Naming Rules

Context Convention Example

Variables, functions camelCase userName , fetchUser()

Types, interfaces, classes PascalCase UserProfile , AuthService

Constants UPPER_SNAKE_CASE MAX_RETRIES , API_BASE_URL

Files kebab-case user-profile.tsx , auth-service.ts

Component files PascalCase UserProfile.tsx , AuthGuard.tsx

Test files Match source UserProfile.test.tsx

Enum members PascalCase UserRole.Admin

Code Quality Rules

  • Zero warnings policy — Treat warnings as errors. Fix them, don't ignore them.

  • No @ts-ignore — Use @ts-expect-error with a comment explaining why, only as a last resort.

  • No type assertions as escape hatches — as unknown as T is a code smell. Refactor instead.

  • Prefer const assertions — Use as const for literal types instead of explicit type annotations.

  • JSDoc for exports — Document exported functions and types with JSDoc. Focus on the "why", not the "what".

/**

  • Encrypts PII fields before database storage.
  • Uses AES-256-GCM with a per-record IV for uniqueness. */ export function encryptField(plaintext: string, key: Buffer): EncryptedField { // ... }

Post-Change Verification (MANDATORY)

After every TypeScript code change, run this 4-step verification. No exceptions.

The 4 Steps

1. Type-check

pnpm run type-check

or per-app: pnpm --filter patient type-check

2. Lint

pnpm run lint

or per-app: pnpm --filter patient lint

3. Format

pnpm run format

or per-app: pnpm --filter patient format

4. Test

pnpm run test

or per-app: pnpm --filter patient test

One-Command Verification

Run all checks for a specific app

pnpm --filter patient validate

Rules

  • All 4 steps must pass before considering a change complete.

  • Fix issues immediately — Don't defer lint warnings or type errors.

  • Never suppress to pass — No @ts-ignore , no biome-ignore , no any casts.

See references/post-change-protocol.md for the full verification workflow, common failures, and troubleshooting.

Reference Files

File Description

references/strict-config.md Full tsconfig.base.json, strict flags, zero-any patterns, anti-patterns

references/type-safety.md Type guards, discriminated unions, branded types, generics, utility types

references/esm-modules.md ESM fundamentals, import/export rules, barrel files, build output

references/error-handling.md Result types, discriminated union errors, async patterns, retry logic

references/biome.md Full biome.json config, rules reference, VS Code integration

references/post-change-protocol.md 4-step verification workflow, troubleshooting, common failures

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

react-writing-code

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

typescript-testing

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

code-documentation

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

dev-workflow

No summary provided by upstream source.

Repository SourceNeeds Review