typescript-best-practices
Scannednpx machina-cli add skill Taoidle/plan-cascade/typescript --openclawFiles (1)
SKILL.md
2.1 KB
TypeScript Best Practices
Code Style
| Rule | Guideline |
|---|---|
| Formatter | Prettier |
| Linter | ESLint + @typescript-eslint |
| Strict mode | strict: true in tsconfig |
Type Safety
| 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;
}
Error Handling
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();
}
Project Structure
src/{index.ts, types/, services/, utils/}
tests/
package.json, tsconfig.json
Async Patterns
| Pattern | Usage |
|---|---|
Promise.all | Parallel operations |
AbortController | Cancellation |
const [user, settings] = await Promise.all([fetchUser(id), fetchSettings(id)]);
Anti-Patterns
| 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';
Testing (Vitest)
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');
});
});
Source
git clone https://github.com/Taoidle/plan-cascade/blob/master/builtin-skills/typescript/SKILL.mdView on GitHub Overview
TypeScript/Node.js best practices for writing safe, maintainable code. Covers type safety, async patterns, and error handling to reduce bugs and improve reliability.
How This Skill Works
This guide compiles concrete rules and patterns from the skill content: enforce strict TypeScript usage, apply type guards and discriminated unions, implement a standardized ApiError for HTTP errors, leverage Promise.all for parallel async work and AbortController for cancellation, and avoid common anti-patterns like as assertions and any.
When to Use It
- When writing TypeScript/Node.js code
- When reviewing code for type safety and avoiding any
- When implementing API clients with standardized error handling
- When building parallel or cancellable async flows
- When auditing projects for maintainability and anti-patterns
Quick Start
- Step 1: Enable strict mode in tsconfig and set up Prettier/ESLint
- Step 2: Add type guards, discriminated unions, and avoid any
- Step 3: Implement ApiError and use Promise.all with AbortController for async; test with Vitest
Best Practices
- Enable strict mode (tsconfig strict: true) to catch issues early
- Use Prettier and ESLint with @typescript-eslint for code quality
- Prefer type guards and discriminated unions; avoid any
- Implement ApiError for API call errors with status codes
- Use Promise.all for parallel async work and AbortController for cancellation; rely on ?. and ?? rather than ! and as
Example Use Cases
- 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(); }
- const [user, settings] = await Promise.all([fetchUser(id), fetchSettings(id)]);
Frequently Asked Questions
Add this skill to your agents