/howto-code-in-typescript
Use when writing TypeScript code, reviewing TS implementations, or making decisions about type declarations, function styles, or naming conventions - comprehensive house style covering type vs interface rules, function declarations, FCIS integration, immutability patterns, and
$ npx -y skills add ed3dai/ed3d-plugins --skill howto-code-in-typescript --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/howto-code-in-typescript
Context preview
The summary Claude sees to decide when to auto-load this skill.
Use when writing TypeScript code, reviewing TS implementations, or making decisions about type declarations, function styles, or naming conventions - comprehensive house style covering type vs interface rules, function declarations, FCIS integration, immutability patterns, and
SKILL.md
howto-code-in-typescript.SKILL.mdname: howto-code-in-typescript
description: Use when writing TypeScript code, reviewing TS implementations, or making decisions about type declarations, function styles, or naming conventions - comprehensive house style covering type vs interface rules, function declarations, FCIS integration, immutability patterns, and type safety enforcement
user-invocable: false
TypeScript House Style
Overview
Comprehensive TypeScript coding standards emphasizing type safety, immutability, and integration with Functional Core, Imperative Shell (FCIS) pattern.
**Core principles:**
- Types as documentation and constraints
- Immutability by default prevents bugs
- Explicit over implicit (especially in function signatures)
- Functional Core returns Results, Imperative Shell may throw
- Configuration over decoration/magic
Quick Self-Check (Use Under Pressure)
When under deadline pressure or focused on other concerns (performance, accuracy, features), STOP and verify:
- [ ] Using `Array<T>` not `T[]`
- [ ] Using `type` not `interface` (unless class contract)
- [ ] Using math.js for money/currencies/complex math
- [ ] Parameters are `readonly` or `Readonly<T>`
- [ ] Using `unknown` not `any`
- [ ] Using `null` for absent values (not `undefined`)
- [ ] Using function declarations (not const arrow) for top-level functions
- [ ] Using named exports (not default exports)
- [ ] Using `===` not `==`
- [ ] Using `.sort((a, b) => a - b)` for numeric arrays
- [ ] Using `parseInt(x, 10)` with explicit radix
**Why this matters:** Under pressure, you'll default to muscle memory. These checks catch the most common violations.
Type Declarations
Type vs Interface
**Always use `type` except for class contracts.**
// GOOD: type for object shapes
type UserData = {
readonly id: string;
name: string;
email: string | null;
};
// GOOD: interface for class contract
interface IUserRepository {
findById(id: string): Promise<User | null>;
}
class UserRepository implements IUserRepository {
// implementation
}
// BAD: interface for object shape
interface UserData {
id: string;
name: string;
}**Rationale:** Types compose better with unions and intersections, support mapped types, and avoid declaration merging surprises. Interfaces are only for defining what a class must implement.
**IMPORTANT:** Even when under deadline pressure, even when focused on other concerns (financial accuracy, performance optimization, bug fixes), take 2 seconds to ask: "Is this a class contract?" If no, use `type`. Don't default to `interface` out of habit.
Naming Conventions
Type Suffixes
| Suffix | Usage | Example | |--------|-------|---------| | `FooOptions` | Function parameter objects (3+ args or any optional) | `ProcessUserOptions` | | `FooConfig` | Persistent configuration from storage | `DatabaseConfig` | | `FooResult` | Discriminated union return types | `ValidationResult` | | `FooFn` | Function/callback types | `TransformFn<T>` | | `FooProps` | React component props | `ButtonProps` | | `FooState` | State objects (component/application) | `AppState` |
General Casing
| Element | Convention | Example | |---------|-----------|---------| | Variables & functions | camelCase | `userName`, `getUser()` | | Types & classes | PascalCase | `UserData`, `UserService` | | Constants | UPPER_CASE | `MAX_RETRY_COUNT`, `API_ENDPOINT` | | Files | kebab-case | `user-service.ts`, `process-order.ts` |
Boolean Naming
**Use is/has/can/should/will prefixes. Avoid negative names.**
// GOOD
const isActive = true;
const hasPermission = checkPermission();
const canEdit = user.role === 'admin';
const shouldRetry = attempts < MAX_RETRIES;
const willTimeout = elapsed > threshold;
// Also acceptable: adjectives for state
type User = {
active: boolean;
visible: boolean;
disabled: boolean;
};
// BAD: negative names
const isDisabled = false; // prefer isEnabled
const notReady = true; // prefer isReadyType Suffix Details
FooOptions - Parameter Objects
**Use for functions with 3+ arguments OR any optional arguments.**
type ProcessUserOptions = {
readonly name: string;
readonly email: string;
readonly age: number;
readonly sendWelcome?: boolean;
};
// GOOD: destructure in body, not in parameters
function processUser(options: ProcessUserOptions): void {
const {name, email, age, sendWelcome = true} = options;
// implementation
}
// BAD: inline destructuring in parameters
function processUser({name, email, age}: {name: string, email: string, age: number}) {
// causes duplication when destructuring
}
// BAD: not using options pattern for 3+ args
function processUser(name: string, email: string, age: number, sendWelcome?: boolean) {
// hard to call, positional arguments
}FooResult - Discriminated Unions
**Always use discriminated unions for Result types. Integrate with neverthrow.**
// GOOD: discriminated union with success/error
type ValidationResult =
| { success: true; data: ValidUser }
| { success: false; error: ValidationError };
// GOOD: use neverthrow for Result types
import {Result, ok, err} from 'neverthrow';
type ValidationError = {
field: string;
message: string;
};
function validateUser(data: Readonly<UserData>): Result<ValidUser, ValidationError> {
if (!data.email) {
return err({field: 'email', message: 'Email is required'});
}
return ok({...data, validated: true});
}
// Usage
const result = validateUser(userData);
if (result.isOk()) {
console.log(result.value); // ValidUser
} else {
console.error(result.error); // ValidationError
}**Rule:** Functional Core functions should return `Result<T, E>` types. Imperative Shell functions may throw exceptions for HTTP errors and similar.
Functions
Declaration Style
**Use `function` declarations for top-level functions. Use arrow functions for inline callbacks.**
// GOOD: function decla
Read more
name: howto-code-in-typescript description: Use when writing TypeScript code, reviewing TS implementations, or making decisions about type declarations, function styles, or naming conventions - comprehensive house style covering type vs interface rules, function declarations, FCIS integration, immutability patterns, and type safety enforcement user-invocable: false
TypeScript House Style
Overview
Comprehensive TypeScript coding standards emphasizing type safety, immutability, and integration with Functional Core, Imperative Shell (FCIS) pattern.
**Core principles:**
- Types as documentation and constraints
- Immutability by default prevents bugs
- Explicit over implicit (especially in function signatures)
- Functional Core returns Results, Imperative Shell may throw
- Configuration over decoration/magic
Quick Self-Check (Use Under Pressure)
When under deadline pressure or focused on other concerns (performance, accuracy, features), STOP and verify:
- [ ] Using `Array<T>` not `T[]`
- [ ] Using `type` not `interface` (unless class contract)
- [ ] Using math.js for money/currencies/complex math
- [ ] Parameters are `readonly` or `Readonly<T>`
- [ ] Using `unknown` not `any`
- [ ] Using `null` for absent values (not `undefined`)
- [ ] Using function declarations (not const arrow) for top-level functions
- [ ] Using named exports (not default exports)
- [ ] Using `===` not `==`
- [ ] Using `.sort((a, b) => a - b)` for numeric arrays
- [ ] Using `parseInt(x, 10)` with explicit radix
**Why this matters:** Under pressure, you'll default to muscle memory. These checks catch the most common violations.
Type Declarations
Type vs Interface
**Always use `type` except for class contracts.**
// GOOD: type for object shapes
type UserData = {
readonly id: string;
name: string;
email: string | null;
};
// GOOD: interface for class contract
interface IUserRepository {
findById(id: string): Promise<User | null>;
}
class UserRepository implements IUserRepository {
// implementation
}
// BAD: interface for object shape
interface UserData {
id: string;
name: string;
}**Rationale:** Types compose better with unions and intersections, support mapped types, and avoid declaration merging surprises. Interfaces are only for defining what a class must implement.
**IMPORTANT:** Even when under deadline pressure, even when focused on other concerns (financial accuracy, performance optimization, bug fixes), take 2 seconds to ask: "Is this a class contract?" If no, use `type`. Don't default to `interface` out of habit.
Naming Conventions
Type Suffixes
| Suffix | Usage | Example | |--------|-------|---------| | `FooOptions` | Function parameter objects (3+ args or any optional) | `ProcessUserOptions` | | `FooConfig` | Persistent configuration from storage | `DatabaseConfig` | | `FooResult` | Discriminated union return types | `ValidationResult` | | `FooFn` | Function/callback types | `TransformFn<T>` | | `FooProps` | React component props | `ButtonProps` | | `FooState` | State objects (component/application) | `AppState` |
General Casing
| Element | Convention | Example | |---------|-----------|---------| | Variables & functions | camelCase | `userName`, `getUser()` | | Types & classes | PascalCase | `UserData`, `UserService` | | Constants | UPPER_CASE | `MAX_RETRY_COUNT`, `API_ENDPOINT` | | Files | kebab-case | `user-service.ts`, `process-order.ts` |
Boolean Naming
**Use is/has/can/should/will prefixes. Avoid negative names.**
// GOOD
const isActive = true;
const hasPermission = checkPermission();
const canEdit = user.role === 'admin';
const shouldRetry = attempts < MAX_RETRIES;
const willTimeout = elapsed > threshold;
// Also acceptable: adjectives for state
type User = {
active: boolean;
visible: boolean;
disabled: boolean;
};
// BAD: negative names
const isDisabled = false; // prefer isEnabled
const notReady = true; // prefer isReadyType Suffix Details
FooOptions - Parameter Objects
**Use for functions with 3+ arguments OR any optional arguments.**
type ProcessUserOptions = {
readonly name: string;
readonly email: string;
readonly age: number;
readonly sendWelcome?: boolean;
};
// GOOD: destructure in body, not in parameters
function processUser(options: ProcessUserOptions): void {
const {name, email, age, sendWelcome = true} = options;
// implementation
}
// BAD: inline destructuring in parameters
function processUser({name, email, age}: {name: string, email: string, age: number}) {
// causes duplication when destructuring
}
// BAD: not using options pattern for 3+ args
function processUser(name: string, email: string, age: number, sendWelcome?: boolean) {
// hard to call, positional arguments
}FooResult - Discriminated Unions
**Always use discriminated unions for Result types. Integrate with neverthrow.**
// GOOD: discriminated union with success/error
type ValidationResult =
| { success: true; data: ValidUser }
| { success: false; error: ValidationError };
// GOOD: use neverthrow for Result types
import {Result, ok, err} from 'neverthrow';
type ValidationError = {
field: string;
message: string;
};
function validateUser(data: Readonly<UserData>): Result<ValidUser, ValidationError> {
if (!data.email) {
return err({field: 'email', message: 'Email is required'});
}
return ok({...data, validated: true});
}
// Usage
const result = validateUser(userData);
if (result.isOk()) {
console.log(result.value); // ValidUser
} else {
console.error(result.error); // ValidationError
}**Rule:** Functional Core functions should return `Result<T, E>` types. Imperative Shell functions may throw exceptions for HTTP errors and similar.
Functions
Declaration Style
**Use `function` declarations for top-level functions. Use arrow functions for inline callbacks.**
// GOOD: function decla
Showing the first part of this file.
This is my collection of plugins that I use on a day-to-day basis for getting stuff done with Claude Code. Most of these are development-oriented in some way or another, but also often end up being useful for other things.
Repo: ed3dai/ed3d-plugins
Other skills on ed3d-plugins.
- /doing-a-simple-two-stage-fanout
Use when analyzing a large corpus of text, code, or data that exceeds a single agent's effective context - orchestrates parallel Worker subagents, Critic review subagents, and a final Summarizer subagent with task tracking and failure recovery
Open skill - /using-generic-agents
Use to decide what kind of generic agent you should use
Open skill - /creating-a-plugin
Use when creating a new Claude Code plugin or setting up plugin structure - provides complete file organization, manifest format, and component definitions for commands, agents, skills, hooks, and MCP servers
Open skill - /creating-an-agent
Use when creating specialized subagents for Claude Code plugins or the Task tool - covers description writing for auto-delegation, tool selection, prompt structure, and testing agents
Open skill - /maintaining-a-marketplace
Use when creating, releasing, or maintaining a Claude Code Plugin Marketplace - covers marketplace.json schema, version management, release checklists, changelog conventions, and validation to prevent sync drift between plugin.json and marketplace.json
Open skill - /maintaining-project-context
Use when completing development phases or branches to identify and update CLAUDE.md or AGENTS.md files that may have become stale - analyzes what changed, determines affected contracts and documentation, and coordinates updates
Open skill

