| name | typescript |
| description | Must use any time a TypeScript file is read or written. |
| author | alexgorbatchev |
TypeScript
Guidelines, conventions, and requirements for writing high-quality, type-safe, and maintainable TypeScript code.
Prohibited Patterns
any type
- Type casting with
as Type (exceptions: branded types, DOM elements, test mocks)
as any
- Inline imports or
require() statements
- Inline type imports such as
import("vite").PluginOption
- Inline structural type expressions when inference or a named type can carry the contract
- Unjustified dynamic imports (
await import()) outside lazy-loading, optional dependency, or plugin boundaries
import * as Foo or export * as Foo
- Unnecessary renaming imports (
import { Foo as Bar }) when there is no name conflict or clarity benefit
File Naming
Filename must match the exact name and casing of the primary exported element:
- Function
createUser → createUser.ts
- Class
UserProfile → UserProfile.ts
- Interface
IUserService → IUserService.ts
- Type
Config → Config.ts
Each file should have a single primary export. If multiple related items are exported, name the file after the most important one.
Special cases:
- Constants:
constants.ts
- Utility collections:
stringUtils.ts, dateUtils.ts
- Type collections:
types.ts (must not contain implementations)
- Index files:
index.ts (re-exports public API only)
- Tests:
{sourceFileName}.test.ts in __tests__/
Also follow these ownership-location rules:
- component ownership
.tsx files live under components/, templates/, or layouts/, using ComponentName.tsx by default or component-name.tsx when the shared config uses FilenameStyle.DashCase
- exported runtime hooks whose names start with
use live in direct-child hooks/useThing.ts[x] files by default or hooks/use-thing.ts[x] when the shared config uses FilenameStyle.DashCase
stories/ directories are reserved for *.stories.tsx, helpers.ts[x], fixtures.ts[x], and fixtures/
__tests__/ directories are reserved for *.test.ts[x], helpers.ts[x], fixtures.ts[x], and fixtures/
Naming Conventions
| Pattern | Use For |
|---|
camelCase | Variables, functions, methods, properties |
PascalCase | Classes, interfaces, types, enums |
SCREAMING_SNAKE_CASE | Constants |
IInterface | Interface names |
kebab-case | CSS classes |
Booleans
Prefix with: is, has, can, should, will, does
const isValid = true;
const hasPermission = false;
const valid = true;
Error Variables
Always use error, never err or e:
catch (error) { }
catch (err) { }
Path Variables
Be consistent within context:
const sourcePath = '/src';
const destinationPath = '/dest';
const sourcePath = '/src';
const dest = '/dest';
Temporary Variables
Avoid unnecessary single-use variables. Keep intermediate variables when they improve clarity, debugging, or narrowing:
console.log(determinePath());
const resolvedPath = determinePath();
logger.debug({ resolvedPath });
const finalPath = determinePath();
console.log(finalPath);
Type Safety
Extract First, Check Second
const item = array[0];
if (item) {
}
array[0] as string;
Type Annotations Over Assertions
const value: ExpectedType = sourceValue;
const value = sourceValue as ExpectedType;
Explicit Return Types
Exported/public functions must declare explicit return types. Local callbacks and obvious local helpers may rely on inference:
export function createUser(name: string): UserResult {
return { id: generateId(), name, createdAt: new Date() };
}
const names = users.map((user) => user.name);
export function createUser(name: string) {
return { id: generateId(), name, createdAt: new Date() };
}
Use Existing Types
Variables not from function calls need explicit types. Prefer existing named types or inference; avoid inline type definitions:
type Metadata = { tarballUrl: string; };
type OperationResult = { success: boolean; metadata: Metadata; };
function getResults(): Promise<OperationResult>;
import type { PluginOption } from 'vite';
const plugin: PluginOption = {};
function getResults(): Promise<{ success: boolean; metadata: { tarballUrl: string; }; }>;
const plugin: import('vite').PluginOption = {};
Type Guards Over Assertions
Use type guard functions for complex/custom types. Built-in type checks (typeof for primitives, Array.isArray) are fine:
function isUserProfile(value: unknown): value is UserProfile {
return typeof value === 'object' && value !== null && 'id' in value && 'name' in value;
}
if (isUserProfile(data)) {
}
if (typeof input === 'string') {}
if (typeof count === 'number') {}
if (Array.isArray(items)) {}
Module Structure
Index File Requirement
Re-export public API through index.ts files at package/module boundaries. Internal/private folders do not need barrel files unless they improve discoverability.
src/
├── index.ts // Public API re-exports only
├── UserService.ts
├── validation/
│ ├── index.ts // Public validation API
│ └── emailValidator.ts
└── internal/
└── formatLog.ts // No index.ts required (private implementation)
Index.ts Export Pattern
Prefer explicit exports in boundary index.ts files for stable public APIs. Wildcard exports are acceptable for small internal modules with low collision risk.
export { createUser } from './createUser';
export { UserService } from './UserService';
export { validateEmail } from './validation';
export * from './validation';
export * from './createUser';
export * from './UserService';
Type/Value Ownership
- Keep
index.ts as a pure barrel instead of mixing in local runtime logic.
- Keep
constants.ts value-only.
- Keep
types.ts type-only.
- Do not import types from
constants.ts.
- Do not leak values from
types.ts.
Functional Programming
Pure Functions
Core logic should be implemented as pure functions where possible. A pure function's output must depend only on its explicit input arguments, and it must not cause side effects (e.g., modifying external state, performing I/O).
Isolate Side Effects
Operations with side effects (e.g., file system access, network requests, direct logging to console/files, reading system env or properties) must be isolated from pure core logic. These side effects should be handled at the "edges" of the application (e.g., in the main entry point, dedicated I/O modules, or specific command handlers).
Dependency Injection
Functions that orchestrate operations but need to invoke side effects must receive the necessary handlers (e.g., FileSystem instance, HTTP client, logger instance) as arguments. No singletons - pass dependencies explicitly. Design for testability - all dependencies should be injectable.
Configuration
Configuration objects derived from external sources (like environment variables) must be created by pure functions. These functions receive all necessary raw inputs (e.g., an object representing environment variables, system properties) as arguments and should use appropriate validation libraries to parse and transform these inputs into a typed configuration object. This validated configuration object is then created at the application's main entry point and passed down via dependency injection.
Tooling
- Critical: Always prefer
tsc as the default compiler and typechecker.
Formatting
- Line length: 120 characters
- No file header comments
- Imports at top of file, before any other code
- Use shortest import path for
@foo/bar packages
- Indent multiline template literals to match the surrounding code
- Use modern TypeScript and ECMAScript features when supported by the project's runtime targets, tsconfig, and polyfill/transpile strategy
Testing
For testing guidelines, patterns, and organization (including conditional logic restrictions, exact string matching, snapshots, and fixtures), see testing.md.
React Development and Testing
When working on React components or tests:
- For component design, JSX requirements, custom hook files, and UI consistency guidelines, see react-development.md.
- For React-specific testing strategies,
act(...) warning failure scheduling, and component logic test patterns, see react-testing.md.
Storybook Development and Testing
When creating, updating, or reviewing Storybook stories or writing play browser interaction tests:
- For story contracts, default typed metadata, play function patterns, and browser execution rules, see storybook.md.