Skip to content
cccskills
FreePublished on GitHub

typescript-dev

TypeScript best practices: strict mode, ESLint rules, file naming, imports, type safety, Zod validation, error handling, and testing with Vitest. Use when writing TypeScript code, reviewing for code quality, fixing lint errors, creating types/interfaces, writing tests, or adding validation.

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md5.7 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

TypeScript Development

Strict Mode Essentials

Both projects use strict: true with these additional flags:

  • noUncheckedIndexedAccess — array/record indexing returns T | undefined, must guard before use
  • noUnusedLocals / noUnusedParameters — prefix unused with _
  • noImplicitReturns, noImplicitOverride, noFallthroughCasesInSwitch, noPropertyAccessFromIndexSignature (erode)
const items = ['a', 'b', 'c'];
const first = items[0]; // type: string | undefined
// ❌ first.toUpperCase()        — might be undefined
// ✅ if (first !== undefined) { first.toUpperCase() }

ESLint Rules

Both projects use @typescript-eslint/strict-type-checked + stylisticTypeChecked.

  • eqeqeq — always use ===/!== (erode allows == null via smart mode; forestry is strict)
  • max-lines: 500 (skipBlankLines, skipComments) — split files when approaching the limit
  • no-unsafe-assignment, no-unsafe-member-access — never bypass with as casts
  • restrict-template-expressions — only strings/numbers in template literals (erode allows numbers)
  • prefer-nullish-coalescing — use ?? over || for nullable values
  • Never suppress with eslint-disable — fix the root cause
// ❌ Loose equality
if (value == '') { ... }

// ✅ Strict equality
if (value === '') { ... }

// ❌ Logical OR for nullable (falsy '' or 0 are lost)
const name = input || 'default';

// ✅ Nullish coalescing (only null/undefined trigger fallback)
const name = input ?? 'default';

File Naming & Imports

Enforced via eslint-plugin-check-file:

  • kebab-case for all .ts/.tsx files and src/ folders
  • .js extensions in all local ESM imports (required by both Node16 and NodeNext resolution)
// ❌ Missing extension
import { validate } from './utils/validation';

// ✅ With .js extension
import { validate } from './utils/validation.js';

// ❌ File naming
src / utils / myHelper.ts;
src / UserService / index.ts;

// ✅ File naming
src / utils / my - helper.ts;
src / user - service / index.ts;

Type Safety

  • Constrained types over raw string/number — use literal unions, enums, or branded types for domain values with known sets
  • unknown over any — then narrow with type guards or instanceof
  • satisfies for type-checked object literals that preserve literal types
  • Before creating types: search existing types, enums, and Zod schemas first — never duplicate
// ❌ function setStatus(status: string) — raw string for known values
// ✅ type Status = 'pending' | 'active' | 'archived';
//    function setStatus(status: Status)

// ❌ function handle(input: any) { return input.name; }
// ✅ function handle(input: unknown) {
//      if (typeof input === 'object' && input !== null && 'name' in input) { ... }
//    }

Zod Validation

Schema-first pattern — define Zod schema, derive type with z.infer<>:

  • z.coerce for string-to-number conversion (CLI args, query params)
  • Wrap schema.parse() in a helper that converts ZodError to project error type
  • Keep schemas in dedicated files (src/schemas/ or src/validation/), not inline in business logic
const ConfigSchema = z.object({
  port: z.coerce.number().min(1).max(65535),
  debug: z.boolean().default(false),
});
type Config = z.infer<typeof ConfigSchema>;

// Wrap parse in helper that converts ZodError to project error type
function validate<T>(schema: z.ZodType<T>, data: unknown): T {
  try {
    return schema.parse(data);
  } catch (error) {
    if (error instanceof z.ZodError) {
      throw new AppError(`Validation failed: ${error.issues.map((i) => i.message).join(', ')}`);
    }
    throw error;
  }
}

Error Handling

Base error class with code (enum), message, and context metadata:

  • Set this.name in constructor and call Error.captureStackTrace
  • static fromError(error: unknown) factory for safely wrapping unknown errors
  • Extend for domain-specific error types (config, API, validation)
class AppError extends Error {
  constructor(
    message: string,
    public readonly code: ErrorCode,
    public readonly context: Record<string, unknown> = {}
  ) {
    super(message);
    this.name = 'AppError';
    Error.captureStackTrace(this, AppError);
  }
  static fromError(error: unknown, code = ErrorCode.UNKNOWN): AppError {
    if (error instanceof AppError) return error;
    if (error instanceof Error)
      return new AppError(error.message, code, { originalError: error.name });
    return new AppError(String(error), code);
  }
}

Testing with Vitest

  • Tests colocated near source in __tests__/ directories
  • Behavior-focused tests — test what the code does, not how
  • Test file naming: *.test.ts
  • Use .js extensions in test imports too

Avoid

  • any type — use unknown and narrow
  • eslint-disable comments — fix the root cause
  • as type assertions to silence errors — use type guards or satisfies
  • Duplicating existing types — search first
  • == comparisons — use === (== null only in smart-mode projects)
  • Files over 500 lines — split into focused modules
  • Non-kebab-case file/folder names
  • Missing .js extension in local imports
  • Inline Zod schemas in business logic — keep in schema files
  • Untyped catch blocks — error is unknown, narrow before use
  • Raw string/number for domain values with known constrained sets

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

adr

Free

Create and manage Architecture Decision Records (ADRs). Use when making architectural changes (refactoring, API versioning, new domain layers, schema changes), when reviewing recent commits for documentation needs, or when the user mentions ADR, architecture decisions, or documenting technical decisions.

The English summary is being prepared. Showing the original description.

erode-app/erode54Updated Oct 15, 2026

Detect documentation and architecture-model drift by comparing source-of-truth code (CLI commands, flags, env vars, GH Action inputs/outputs, package structure, pipeline stages, providers, adapters, platforms) against web docs, README, and LikeC4 architecture model (.c4 files). Use when files that define the user-facing interface or internal structure change, when the user asks to check docs, or when mentioning doc drift, architecture drift, stale docs, or documentation sync.

The English summary is being prepared. Showing the original description.

erode-app/erode54Updated Oct 15, 2026

Enforce the project's documentation voice when writing or reviewing public-facing content. Triggers on edits to doc files (packages/web/src/content/docs/**/*.md) and README.md. Catches LLM prose patterns, marketing language, and messaging drift.

The English summary is being prepared. Showing the original description.

erode-app/erode54Updated Oct 15, 2026

Use this skill whenever code changes introduce new imports, API calls, database connections, message queues, or service-to-service communication. Also use before any commit or push. Catches undeclared dependencies and architecture violations using Erode.

The English summary is being prepared. Showing the original description.

erode-app/erode54Updated Oct 15, 2026

Generate product pitch videos for the Erode project from screenshots. Use this skill whenever the user wants to create, update, or regenerate the product demo video, landing page animation, or pitch clip for erode.dev or the GitHub repo. Also trigger when the user mentions "product video", "demo video", "pitch video", "update the video", "regenerate video", or "landing page animation" in the context of Erode.

The English summary is being prepared. Showing the original description.

erode-app/erode54Updated Oct 15, 2026

Conventional commits with project conventions. Use when the user asks to commit, make a commit, or wants to follow the project's git commit standard.

The English summary is being prepared. Showing the original description.

erode-app/erode54Updated Oct 15, 2026

See all skills by erode-app

Report a problem with this skill