Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    nervaya

    typescript-standards

    nervaya/typescript-standards
    Coding
    1 installs

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    TypeScript coding standards based on Google TypeScript Style Guide. Use when writing TypeScript code, defining types, working with imports/exports, or ensuring type safety.

    SKILL.md

    TypeScript Standards

    Source File Structure

    Files must be organized in this order:

    1. Copyright/license (if present)
    2. @fileoverview JSDoc (if present)
    3. Imports
    4. Implementation

    Exactly one blank line separates each section.

    Imports

    Import Types

    // ✅ Module imports - for many symbols from a module
    import * as mongoose from 'mongoose';
    import * as path from 'path';
    
    // ✅ Named imports - for specific symbols
    import { Injectable, Controller, Get } from '@nestjs/common';
    import { PurchaseService, PurchaseQueryService } from './services';
    
    // ✅ Use import type when symbol is only used as a type (no runtime load)
    import type { Foo } from './foo';
    import { Bar } from './foo';  // Bar is a value
    import { type Foo, Bar } from './foo';  // Inline type import
    
    // ✅ Default imports - only when required by external code
    import Button from 'Button';
    
    // ✅ Side-effect imports - only for libraries that need side effects
    import 'reflect-metadata';
    

    Import Order

    // 1. Node built-ins
    import * as path from 'path';
    import * as fs from 'fs';
    
    // 2. External packages
    import { Injectable } from '@nestjs/common';
    import { InjectModel } from '@nestjs/mongoose';
    
    // 3. Internal absolute imports
    import { ApiTagsConstants } from 'src/shared/constants';
    import { JwtPayload } from 'src/v2/auth/interfaces';
    
    // 4. Relative imports
    import { PurchaseService } from './purchase.service';
    import { CreatePurchaseDto } from './dtos';
    

    Import Best Practices

    // ✅ Use relative imports for same project files
    import { PurchaseService } from './purchase.service';
    import { CreateDto } from '../dtos';
    
    // ✅ Prefer named imports for common symbols
    import { describe, it, expect } from '@jest/globals';
    
    // ✅ Use namespace imports for many symbols
    import * as purchaseUtils from './utils/purchase.utils';
    
    // ❌ Don't use wildcard imports when only using few symbols
    import * as nestCommon from '@nestjs/common'; // BAD if only using Injectable
    

    Exports

    Named Exports (Preferred)

    // ✅ Use named exports
    export class PurchaseService { }
    export function calculateTotal() { }
    export const PURCHASE_CONFIG = { };
    
    // ❌ Don't use default exports
    export default class PurchaseService { } // BAD
    

    Barrel Exports

    // services/index.ts
    export * from './purchase-crud.service';
    export * from './purchase-query.service';
    export * from './purchase-validation.service';
    
    // Usage
    import { PurchaseCrudService, PurchaseQueryService } from './services';
    

    Type Definitions

    Interfaces vs Type Aliases

    // ✅ Use interfaces for object shapes
    interface Purchase {
      id: string;
      amount: number;
      status: PurchaseStatus;
      createdAt: Date;
    }
    
    // ✅ Use type aliases for unions, primitives, tuples
    type PurchaseStatus = 'pending' | 'completed' | 'cancelled';
    type PurchaseId = string;
    type Coordinates = [number, number];
    
    // ✅ Use type for complex types
    type PurchaseWithTotal = Purchase & { total: number };
    type AsyncPurchase = Promise<Purchase>;
    

    Interface Naming

    // ✅ Don't use I prefix
    interface Purchase { }
    interface PurchaseService { }
    
    // ❌ Don't use I prefix (Hungarian notation)
    interface IPurchase { }  // BAD
    interface IPurchaseService { }  // BAD
    

    Type Inference

    Let TypeScript Infer When Obvious

    // ✅ Type is obvious from initialization
    const count = 0;
    const name = 'Medicine';
    const isActive = true;
    const items = new Set<string>();
    
    // ❌ Unnecessary explicit type
    const count: number = 0;  // BAD - obvious
    const name: string = 'Medicine';  // BAD - obvious
    

    Explicit Types When Needed

    // ✅ Explicit type for empty collections
    const items: Purchase[] = [];
    const cache: Map<string, Purchase> = new Map();
    
    // ✅ Explicit return types for public APIs
    export function calculateTotal(items: PurchaseItem[]): number {
      return items.reduce((sum, item) => sum + item.price, 0);
    }
    
    // ✅ Explicit types for complex expressions
    const value: PurchaseWithMeta = await rpc.getSomeValue().transform();
    

    Null and Undefined

    Optional vs Undefined

    // ✅ Use optional for optional properties
    interface PurchaseQuery {
      status?: PurchaseStatus;  // Optional
      dateRange?: DateRange;
    }
    
    // ✅ Use | null when null is a valid value
    interface ApiResponse {
      data: Purchase | null;  // Explicitly nullable
      error: string | null;
    }
    
    // ✅ Use optional chaining
    const name = purchase?.customer?.name ?? 'Unknown';
    
    // ✅ Use nullish coalescing
    const quantity = purchase.quantity ?? 0;
    

    Avoiding any

    Use unknown Instead of any

    // ❌ Don't use any
    function processData(data: any) {
      return data.value;  // Unsafe!
    }
    
    // ✅ Use unknown and narrow the type
    function processData(data: unknown): number {
      if (isValidData(data)) {
        return data.value;
      }
      throw new Error('Invalid data');
    }
    
    function isValidData(data: unknown): data is { value: number } {
      return (
        typeof data === 'object' &&
        data !== null &&
        'value' in data &&
        typeof (data as any).value === 'number'
      );
    }
    

    When any is Necessary

    // If any is truly needed, document why
    // This API returns dynamic JSON that varies by endpoint
    // tslint:disable-next-line:no-any
    const response = await externalApi.get() as any;
    

    TypeScript Compiler Directives

    No @ts-ignore or @ts-expect-error

    // ❌ Don't suppress compiler errors
    // @ts-ignore
    const x = badCode();
    
    // ✅ Fix the underlying type issue
    // ✅ If suppression is unavoidable, document why and prefer type assertion with comment
    // payload is { userId, role } from jwtVerify - jose types are generic
    const payload = result.payload as { userId: string; role: string };
    

    Prefer fixing types over suppressing. Use @ts-expect-error only in unit tests when testing APIs that intentionally pass invalid types, and document the reason.

    Classes

    Class Structure

    @Injectable()
    export class PurchaseService {
      // 1. Static members
      private static readonly MAX_RETRIES = 3;
      
      // 2. Instance fields
      private readonly logger = new Logger(PurchaseService.name);
      
      // 3. Constructor
      constructor(
        private readonly crudService: PurchaseCrudService,
        private readonly queryService: PurchaseQueryService,
      ) {}
      
      // 4. Public methods
      async create(dto: CreatePurchaseDto, user: JwtPayload): Promise<Purchase> {
        return this.crudService.create(dto, user);
      }
      
      // 5. Private methods
      private validateInput(dto: CreatePurchaseDto): void {
        // ...
      }
    }
    

    Use readonly for Immutable Properties

    class PurchaseService {
      // ✅ Mark as readonly
      private readonly model: Model<PurchaseDocument>;
      private readonly logger: Logger;
      
      constructor(
        @InjectModel(Purchase.name)
        private readonly purchaseModel: Model<PurchaseDocument>,
      ) {
        this.model = purchaseModel;
        this.logger = new Logger(PurchaseService.name);
      }
    }
    

    Parameter Properties

    // ✅ Use parameter properties for DI
    class PurchaseService {
      constructor(
        private readonly crudService: PurchaseCrudService,
        private readonly queryService: PurchaseQueryService,
      ) {}
    }
    
    // Instead of:
    class PurchaseService {
      private readonly crudService: PurchaseCrudService;
      private readonly queryService: PurchaseQueryService;
      
      constructor(crudService: PurchaseCrudService, queryService: PurchaseQueryService) {
        this.crudService = crudService;
        this.queryService = queryService;
      }
    }
    

    Functions

    Arrow Functions vs Function Declarations

    // ✅ Function declarations for named functions
    function calculateTotal(items: PurchaseItem[]): number {
      return items.reduce((sum, item) => sum + item.price, 0);
    }
    
    // ✅ Arrow functions for callbacks
    items.map(item => item.price);
    items.filter(item => item.price > 100);
    
    // ✅ Arrow functions when type annotation needed
    const processItem: (item: Item) => ProcessedItem = item => {
      // ...
    };
    

    Default Parameters

    // ✅ Use default parameters
    function createPurchase(
      items: PurchaseItem[],
      discount = 0,
      taxRate = 0.18,
    ): Purchase {
      // ...
    }
    
    // ✅ Put default parameters last
    function query(filters: Filters, page = 1, limit = 10) {
      // ...
    }
    

    Error Handling

    Throw Only Errors

    // ✅ Always use new Error() - never call Error() without new
    throw new Error('Purchase not found');
    throw new HttpException('Validation failed', HttpStatus.BAD_REQUEST);
    
    // ❌ Don't use Error() without new
    throw Error('Purchase not found');  // BAD - inconsistent with object instantiation
    
    // ❌ Don't throw strings or other values
    throw 'Something went wrong';  // BAD
    throw { error: 'failed' };  // BAD
    

    Catch Handling

    // ✅ Type catch as unknown
    try {
      await purchaseService.create(dto);
    } catch (error: unknown) {
      if (error instanceof HttpException) {
        // Handle HTTP error
      } else if (error instanceof Error) {
        // Handle generic error
        logger.error(error.message);
      }
      throw error;
    }
    

    Array Types

    // ✅ Use T[] for simple types
    const items: string[] = [];
    const purchases: Purchase[] = [];
    
    // ✅ Use Array<T> for complex types
    const results: Array<{ id: string; value: number }> = [];
    const callbacks: Array<(item: Item) => void> = [];
    
    // ✅ Use readonly for immutable arrays
    function process(items: readonly PurchaseItem[]): void {
      // items cannot be modified
    }
    

    Control Flow

    Use === and !==

    // ✅ Always use strict equality
    if (status === 'completed') { }
    if (count !== 0) { }
    
    // ❌ Don't use loose equality
    if (status == 'completed') { }  // BAD
    if (count != 0) { }  // BAD
    
    // Exception: null checks for both null and undefined
    if (value == null) {
      // value is null or undefined
    }
    

    Prefer for...of

    // ✅ Use for...of for arrays
    for (const item of items) {
      process(item);
    }
    
    // ✅ Use for...of with Object.entries for objects
    for (const [key, value] of Object.entries(obj)) {
      process(key, value);
    }
    
    // ❌ Avoid for...in for arrays
    for (const i in items) {  // BAD - gives string indices
      process(items[i]);
    }
    

    Best Practices Summary

    1. Named exports: Prefer named exports over default exports
    2. Import type: Use import type when symbol is only used as a type
    3. Interfaces: Use interfaces for object shapes, types for unions
    4. Type inference: Let TypeScript infer obvious types
    5. No any: Use unknown and type guards instead
    6. No @ts-ignore: Fix types; suppress only when necessary and document
    7. Strict equality: Use === and !==; exception: value == null for null/undefined checks
    8. Readonly: Mark immutable properties as readonly
    9. Error instantiation: Always throw new Error(), never throw Error() or primitives
    10. Optional chaining: Use ?. and ?? for null safety
    Recommended Servers
    Vercel Grep
    Vercel Grep
    Google Docs
    Google Docs
    OpenZeppelin
    OpenZeppelin
    Repository
    nervaya/nervaya
    Files