coding-style
TypeScript/JavaScript Coding Style
This file extends with TypeScript/JavaScript specific content.
Types and Interfaces
Use types to make public APIs, shared models, and component props explicit, readable, and reusable.
Source Types
Use source types from the owning module or library. Do not create local duplicate types or substitute conversions just to work around TypeScript issues.
Return Types
Declare return types for public contracts. For local or private functions, rely on inference unless TypeScript cannot infer correctly or the annotation documents an important constraint.
Public APIs
- Add parameter and return types to exported functions, shared utilities, and public class methods
- Let TypeScript infer obvious local variable types
- Extract repeated inline object shapes into named types or interfaces
typescript
// WRONG: Exported function without explicit types
export function formatUser(user) {
return `${user.firstName} ${user.lastName}`
}
// CORRECT: Explicit types on public APIs
interface User {
firstName: string
lastName: string
}
export function formatUser(user: User): string {
return `${user.firstName} ${user.lastName}`
}
Interfaces vs. Type Aliases
- Use
interfacefor object shapes that may be extended or implemented - Use
typefor unions, intersections, tuples, mapped types, and utility types - Prefer string literal unions over
enumunless anenumis required for interoperability
typescript
interface User {
id: string
email: string
}
type UserRole = 'admin' | 'member'
type UserWithRole = User & {
role: UserRole
}
Avoid any
- Avoid
anyin application code - Use
unknownfor external or untrusted input, then narrow it safely - Use generics when a value's type depends on the caller
typescript
// WRONG: any removes type safety
function getErrorMessage(error: any) {
return error.message
}
// CORRECT: unknown forces safe narrowing
function getErrorMessage(error: unknown): string {
if (error instanceof Error) {
return error.message
}
return 'Unexpected error'
}
React Props
- Define component props with a named
interfaceortype - Type callback props explicitly
- Do not use
React.FCunless there is a specific reason to do so
typescript
interface User {
id: string
email: string
}
interface UserCardProps {
user: User
onSelect: (id: string) => void
}
function UserCard({ user, onSelect }: UserCardProps) {
return <button onClick={() => onSelect(user.id)}>{user.email}</button>
}
JavaScript Files
- In
.jsand.jsxfiles, use JSDoc when types improve clarity and a TypeScript migration is not practical - Keep JSDoc aligned with runtime behavior
javascript
/**
* @param {{ firstName: string, lastName: string }} user
* @returns {string}
*/
export function formatUser(user) {
return `${user.firstName} ${user.lastName}`
}
Immutability
Use spread operator for immutable updates:
typescript
interface User {
id: string
name: string
}
// WRONG: Mutation
function updateUser(user: User, name: string): User {
user.name = name // MUTATION!
return user
}
// CORRECT: Immutability
function updateUser(user: Readonly<User>, name: string): User {
return {
...user,
name
}
}
Error Handling
Use async/await with try-catch and narrow unknown errors safely:
typescript
interface User {
id: string
email: string
}
declare function riskyOperation(userId: string): Promise<User>
function getErrorMessage(error: unknown): string {
if (error instanceof Error) {
return error.message
}
return 'Unexpected error'
}
const logger = {
error: (message: string, error: unknown) => {
// Replace with your production logger (for example, pino or winston).
}
}
async function loadUser(userId: string): Promise<User> {
try {
const result = await riskyOperation(userId)
return result
} catch (error: unknown) {
logger.error('Operation failed', error)
throw new Error(getErrorMessage(error))
}
}
Input Validation
Use Zod for schema-based validation and infer types from the schema:
typescript
import { z } from 'zod'
const userSchema = z.object({
email: z.string().email(),
age: z.number().int().min(0).max(150)
})
type UserInput = z.infer<typeof userSchema>
const validated: UserInput = userSchema.parse(input)
Console.log
- No
console.logstatements in production code - Use proper logging libraries instead
- See hooks for automatic detection