Claude Code Plugins

Community-maintained marketplace

Feedback

typescript-engineering

@gonzaloserrano/dotfiles
3
0

Comprehensive TypeScript engineering guidelines based on Google's TypeScript Style Guide. This skill should be used when writing TypeScript code, performing TypeScript code reviews, or answering questions about TypeScript best practices. Applies to .ts and .tsx files, and TypeScript configuration.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name typescript-engineering
description Comprehensive TypeScript engineering guidelines based on Google's TypeScript Style Guide. This skill should be used when writing TypeScript code, performing TypeScript code reviews, or answering questions about TypeScript best practices. Applies to .ts and .tsx files, and TypeScript configuration.

TypeScript Engineering

Comprehensive guidelines for writing production-quality TypeScript based on Google's TypeScript Style Guide.

Naming Conventions

Type Convention Example
Classes, Interfaces, Types, Enums UpperCamelCase UserService, HttpClient
Variables, Parameters, Functions lowerCamelCase userName, processData
Global Constants, Enum Values CONSTANT_CASE MAX_RETRIES, Status.ACTIVE
Type Parameters Single letter or UpperCamelCase T, ResponseType

Naming Principles

  • Descriptive names, avoid ambiguous abbreviations
  • Treat acronyms as words: loadHttpUrl not loadHTTPURL
  • No prefixes like opt_ for optional parameters
  • No trailing underscores for private properties
  • Single-letter variables only when scope is <10 lines

Variable Declarations

// Always use const by default
const users = getUsers();

// Use let only when reassignment is needed
let count = 0;
count++;

// Never use var
// var x = 1;  // WRONG

// One variable per declaration
const a = 1;
const b = 2;
// const a = 1, b = 2;  // WRONG

Types and Interfaces

Prefer Interfaces Over Type Aliases

// Good: interface for object shapes
interface User {
  id: string;
  name: string;
  email?: string;
}

// Avoid: type alias for object shapes
type User = {
  id: string;
  name: string;
};

// Type aliases OK for unions, intersections, mapped types
type Status = 'active' | 'inactive';
type Combined = TypeA & TypeB;

Type Inference

Leverage inference for trivially inferred types:

// Good: inference is clear
const name = 'Alice';
const items = [1, 2, 3];

// Good: explicit for complex expressions
const result: ProcessedData = complexTransformation(input);

Array Types

// Simple types: use T[]
const numbers: number[];
const names: readonly string[];

// Multi-dimensional: use T[][]
const matrix: number[][];

// Complex types: use Array<T>
const handlers: Array<(event: Event) => void>;

Null and Undefined

// Prefer optional fields over union with undefined
interface Config {
  timeout?: number;        // Good
  // timeout: number | undefined;  // Avoid
}

// Type aliases must NOT include |null or |undefined
type UserId = string;  // Good
// type UserId = string | null;  // WRONG

// May use == for null comparison (catches both null and undefined)
if (value == null) {
  // handles both null and undefined
}

Types to Avoid

// Avoid any - use unknown instead
function parse(input: unknown): Data { }

// Avoid {} - use unknown, Record<string, T>, or object
function process(obj: Record<string, unknown>): void { }

// Use lowercase primitives
let name: string;    // Good
// let name: String;  // WRONG

// Never use wrapper objects
// new String('hello')  // WRONG

Classes

Structure

class UserService {
  // Fields first, initialized where declared
  private readonly cache = new Map<string, User>();
  private lastAccess: Date | null = null;

  // Constructor with parameter properties
  constructor(
    private readonly api: ApiClient,
    private readonly logger: Logger,
  ) {}

  // Methods separated by blank lines
  async getUser(id: string): Promise<User> {
    // ...
  }

  private validateId(id: string): boolean {
    // ...
  }
}

Visibility

class Example {
  // private by default, only use public when needed externally
  private internalState = 0;

  // readonly for properties never reassigned after construction
  readonly id: string;

  // Never use #private syntax - use TypeScript visibility
  // #field = 1;  // WRONG
  private field = 1;  // Good
}

Avoid Arrow Functions as Properties

class Handler {
  // Avoid: arrow function as property
  // handleClick = () => { ... };

  // Good: instance method
  handleClick(): void {
    // ...
  }
}

// Bind at call site if needed
element.addEventListener('click', () => handler.handleClick());

Static Methods

  • Never use this in static methods
  • Call on defining class, not subclasses

Functions

Prefer Function Declarations

// Good: function declaration for named functions
function processData(input: Data): Result {
  return transform(input);
}

// Arrow functions when type annotation needed
const handler: EventHandler = (event) => {
  // ...
};

Arrow Function Bodies

// Concise body only when return value is used
const double = (x: number) => x * 2;

// Block body when return should be void
const log = (msg: string) => {
  console.log(msg);
};

Parameters

// Use rest parameters, not arguments
function sum(...numbers: number[]): number {
  return numbers.reduce((a, b) => a + b, 0);
}

// Destructuring for multiple optional params
interface Options {
  timeout?: number;
  retries?: number;
}
function fetch(url: string, { timeout = 5000, retries = 3 }: Options = {}) {
  // ...
}

// Never name a parameter 'arguments'

Imports and Exports

Always Use Named Exports

// Good: named exports
export function processData() { }
export class UserService { }
export interface Config { }

// Never use default exports
// export default class UserService { }  // WRONG

Import Styles

// Module import for large APIs
import * as fs from 'fs';

// Named imports for frequently used symbols
import { readFile, writeFile } from 'fs/promises';

// Type-only imports when only used as types
import type { User, Config } from './types';

Module Organization

  • Use modules, never namespace Foo { }
  • Never use require() - use ES6 imports
  • Use relative imports within same project
  • Avoid excessive ../../../

Control Structures

Always Use Braces

// Good
if (condition) {
  doSomething();
}

// Exception: single-line if
if (condition) return early;

Loops

// Prefer for...of for arrays
for (const item of items) {
  process(item);
}

// Use Object methods with for...of for objects
for (const [key, value] of Object.entries(obj)) {
  // ...
}

// Never use unfiltered for...in on arrays

Equality

// Always use === and !==
if (a === b) { }

// Exception: == null catches both null and undefined
if (value == null) { }

Switch Statements

switch (status) {
  case Status.Active:
    handleActive();
    break;
  case Status.Inactive:
    handleInactive();
    break;
  default:
    // Always include default, even if empty
    break;
}

Exception Handling

// Always throw Error instances
throw new Error('Something went wrong');
// throw 'error';  // WRONG

// Catch with unknown type
try {
  riskyOperation();
} catch (e: unknown) {
  if (e instanceof Error) {
    logger.error(e.message);
  }
  throw e;
}

// Empty catch needs justification comment
try {
  optional();
} catch {
  // Intentionally ignored: fallback behavior handles this
}

Type Assertions

// Use 'as' syntax, not angle brackets
const input = value as string;
// const input = <string>value;  // WRONG in TSX, avoid everywhere

// Double assertion through unknown when needed
const config = (rawData as unknown) as Config;

// Add comment explaining why assertion is safe
const element = document.getElementById('app') as HTMLElement;
// Safe: element exists in index.html

Strings

// Use single quotes for string literals
const name = 'Alice';

// Template literals for interpolation or multiline
const message = `Hello, ${name}!`;
const query = `
  SELECT *
  FROM users
  WHERE id = ?
`;

// Never use backslash line continuations

Disallowed Features

Feature Alternative
var const or let
Array() constructor [] literal
Object() constructor {} literal
any type unknown
namespace modules
require() import
Default exports Named exports
#private fields private modifier
eval() Never use
const enum Regular enum
debugger Remove before commit
with Never use
Prototype modification Never modify

Quick Reference

// File structure order:
// 1. Copyright (if present)
// 2. @fileoverview JSDoc (if present)
// 3. Imports
// 4. Implementation

// Prefer interfaces for object types
interface User { }

// Named exports only
export function process() { }
export class Service { }

// const by default, let when needed
const x = 1;
let y = 2;

// Strict equality
if (a === b) { }

// Unknown over any
function parse(data: unknown) { }

// Throw Error instances
throw new Error('message');