TypeScript/Node.js best practices. Use when writing or reviewing TypeScript code. Covers type safety, async patterns, and error handling.
| Rule | Guideline |
|---|---|
| Formatter | Prettier |
| Linter | ESLint + @typescript-eslint |
| Strict mode | strict: true in tsconfig |
| Rule | Guideline |
|---|---|
Avoid any | Use unknown + narrowing |
| Type guards | Custom predicates |
| Discriminated unions | For variants |
type Result<T> = { success: true; data: T } | { success: false; error: Error };
function isUser(v: unknown): v is User {
return typeof v === 'object' && v !== null && 'id' in v;
}
class ApiError extends Error {
constructor(message: string, public status: number) {
super(message);
this.name = 'ApiError';
}
}
async function fetchUser(id: string): Promise<User> {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new ApiError('Failed', res.status);
return res.json();
}
src/{index.ts, types/, services/, utils/}
tests/
package.json, tsconfig.json
| Pattern | Usage |
|---|---|
Promise.all | Parallel operations |
AbortController | Cancellation |
const [user, settings] = await Promise.all([fetchUser(id), fetchSettings(id)]);
| Avoid | Use Instead |
|---|---|
as assertions | Type guards |
! non-null | ?. and ?? |
any | unknown + narrowing |
// Bad: data as User, user!.name
// Good:
if (isUser(data)) { /* data is User */ }
const name = user?.name ?? 'Unknown';
describe('Service', () => {
it('should create', async () => {
const mock = { save: vi.fn().mockResolvedValue({ id: '1' }) };
const result = await new Service(mock).create({ name: 'Test' });
expect(result.id).toBe('1');
});
});