| name | typescript-best-practices |
| description | Use this skill when working with TypeScript code, type definitions, interfaces, generics, or async patterns. |
| version | 1.0.0 |
TypeScript Best Practices
Guidance for writing type-safe, maintainable TypeScript code. Covers type safety fundamentals, async/await patterns, and null handling strategies.
Core Principles
- Strict mode always: Enable
strict: true in tsconfig.json
- Explicit over implicit: Prefer explicit types for public APIs
- Narrow types: Use discriminated unions over loose object types
- Null safety: Handle null/undefined explicitly with strict null checks
- Immutability: Prefer
readonly and const where possible
Type Safety
Prefer Interfaces for Object Shapes
interface User {
id: string;
name: string;
email: string;
}
type UserRole = 'admin' | 'user' | 'guest';
type UserWithRole = User & { role: UserRole };
Use Discriminated Unions for Variants
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
function handleResult<T>(result: Result<T>) {
if (result.success) {
return result.data;
}
throw result.error;
}
Avoid any - Use unknown Instead
function processData(data: any) {
return data.value;
}
function processData(data: unknown) {
if (isValidData(data)) {
return data.value;
}
throw new Error('Invalid data');
}
function isValidData(data: unknown): data is { value: string } {
return typeof data === 'object' && data !== null && 'value' in data;
}
Generic Constraints
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
interface Repository<T extends { id: string } = { id: string }> {
findById(id: string): Promise<T | null>;
save(entity: T): Promise<T>;
}
Async Patterns
Always Await in Try-Catch
async function fetchUser(id: string): Promise<User> {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Failed to fetch user:', error);
throw error;
}
}
Use Promise.all for Parallel Operations
async function fetchUserData(userId: string) {
const [user, posts, comments] = await Promise.all([
fetchUser(userId),
fetchUserPosts(userId),
fetchUserComments(userId),
]);
return { user, posts, comments };
}
Type Async Return Values Explicitly
async function createUser(data: CreateUserDTO): Promise<User> {
const user = await db.users.create(data);
return user;
}
async function safeCreateUser(data: CreateUserDTO): Promise<Result<User>> {
try {
const user = await db.users.create(data);
return { success: true, data: user };
} catch (error) {
return { success: false, error: error as Error };
}
}
Null Handling
Use Optional Chaining and Nullish Coalescing
const userName = user?.profile?.name ?? 'Anonymous';
const config = options?.timeout ?? DEFAULT_TIMEOUT;
const userName = user && user.profile && user.profile.name || 'Anonymous';
Non-Null Assertion Only When Certain
const element = document.getElementById('root')!;
const element = document.getElementById('root');
if (!element) {
throw new Error('Root element not found');
}
Type Guards for Runtime Checks
function isDefined<T>(value: T | null | undefined): value is T {
return value !== null && value !== undefined;
}
const values = [1, null, 2, undefined, 3];
const defined = values.filter(isDefined);
Prefer undefined Over null for Optional
interface Options {
timeout?: number;
retries?: number;
}
interface Options {
timeout: number | null;
retries?: number;
}
Utility Types
Common Utility Type Patterns
type PartialUser = Partial<User>;
type RequiredUser = Required<User>;
type UserCredentials = Pick<User, 'email' | 'password'>;
type PublicUser = Omit<User, 'password'>;
type ImmutableUser = Readonly<User>;
type UserCache = Record<string, User>;
Extract and Exclude for Union Types
type AllRoles = 'admin' | 'user' | 'guest' | 'superadmin';
type AdminRoles = Extract<AllRoles, 'admin' | 'superadmin'>;
type NonAdminRoles = Exclude<AllRoles, 'admin' | 'superadmin'>;
Quick Reference
tsconfig.json Essentials
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true
}
}
Checklist