Receber por email
← Voltar para o catálogo

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 type safety enforcement

235estrelas
Atualizado há 4 meses

Ver no GitHub ↗

Como adicionar

/plugin marketplace add ed3dai/ed3d-plugins

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/howto-code-in-typescript/svg)](https://www.skillteca.com.br/skills/howto-code-in-typescript)

Skills relacionadas

Ver todas de Pesquisa e Web →

Alerta por categoria

Receba novas skills de Pesquisa e Web toda segunda

1 email curto, só com as skills novas de Pesquisa e Web. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

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 isReady

Type 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 declaration for top-level
function processUser(data: Readonly<UserData>): ProcessResult {
  return {success: true, user: data};
}

// GOOD: arrow functions for inline callbacks
const users = rawData.map(u => transformUser(u));
button.addEventListener('click', (e) => handleClick(e));
fetch(url).then(data => processData(data));

// BAD: const arrow for top-level function
const processUser = (data: UserData): ProcessResult => {
  return {success: true, user: data};
};

Rationale: Function declarations are hoisted and more visible. Arrow functions capture lexical this and are concise for callbacks.

Const Arrow Functions

Use const foo = () => {} declarations only for stable references.

// GOOD: stable reference for React hooks
const handleSubmit = (event: FormEvent) => {
  event.preventDefault();
  // implementation
};

useEffect(() => {
  // handleSubmit reference is stable
}, [handleSubmit]);

// GOOD: long event listener passed from variable
const handleComplexClick = (event: MouseEvent) => {
  // many lines of logic
};
element.addEventListener('click', handleComplexClick);

// BAD: const arrow for regular top-level function
const calculateTotal = (items: Array<Item>): number => {
  return items.reduce((sum, item) => sum + item.price, 0);
};

// GOOD: use function declaration
function calculateTotal(items: ReadonlyArray<Item>): number {
  return items.reduce((sum, item) => sum + item.price, 0);
}

Parameter Objects

Use parameter objects for 3+ arguments OR any optional arguments.

// GOOD: options object for 3+ args
type CreateUserOptions = {
  readonly name: string;
  readonly email: string;
  readonly age: number;
  readonly newsletter?: boolean;
};

function createUser(options: CreateUserOptions): User {
  const {name, email, age, newsletter = false} = options;
  // implementation
}

// GOOD: 2 args, but one is optional - use options
type SendEmailOptions = {
  readonly to: string;
  readonly subject: string;
  readonly body?: string;
};

function sendEmail(options: SendEmailOptions): void {
  // implementation
}

// GOOD: 2 required args - no options needed
function divide(numerator: number, denominator: number): number {
  return numerator / denominator;
}

Async Functions

Always explicitly type Promise returns. Avoid async void.

// GOOD: explicit
Compartilhar X LinkedIn