TypeScript
Use TypeScript to enhance safety, clarity, and developer experience.
TypeScript helps catch errors early, improve code readability, and provide powerful tooling support. Proper use of types, generics, enums, and interfaces leads to more maintainable and scalable applications.
Use TypeScript’s type system — don’t bypass it
Avoid using any
or casting types unless absolutely necessary. Let TypeScript help you catch errors through proper typings.
1function process(data: any) {
2 const user = data as User;
3 return user.id;
4}
1function process(data: unknown): number {
2 if (!isUser(data)) throw new Error("Invalid input");
3 return data.id;
4}
Use built-in utility types when possible
TypeScript provides many built-in utility types (Partial
, Pick
, Omit
, etc.) that make type transformations cleaner and safer.
1type UserPreview = {
2 name: string;
3 age: number;
4};
type UserPreview = Pick<User, 'name' | 'age'>;
Prefer union types over enums for simple sets
For short, fixed sets of values, use union types ('draft' | 'published'
) instead of enum
. They're lighter, simpler, and fully type-safe.
1enum Status {
2 Draft = 'draft',
3 Published = 'published',
4}
type Status = 'draft' | 'published';
Write custom type guards to narrow unknown types
For input validation or unknown sources (e.g., API responses), write reusable type guards that protect downstream logic.
1function printUser(user: any) {
2 if (user && user.id) {
3 console.log(user.name);
4 }
5}
1function isUser(obj: unknown): obj is User {
2 return typeof obj === 'object' && obj !== null && 'id' in obj;
3}
4
5function printUser(user: unknown) {
6 if (isUser(user)) {
7 console.log(user.name);
8 }
9}
Explicitly type return values in exported functions
Always specify return types for functions that are exported or reused. This improves type inference, refactoring safety, and readability.
1export function getUser(id: string) {
2 return db.find(id);
3}
1export function getUser(id: string): Promise<User | null> {
2 return db.find(id);
3}
Prefer 'keyof', 'Record', and indexed types for flexibility
Use advanced TS utilities like keyof
and Record
to build type-safe dynamic structures.
1const roles: { [key: string]: boolean } = {
2 admin: true,
3 user: false
4};
1type Role = 'admin' | 'user';
2const roles: Record<Role, boolean> = {
3 admin: true,
4 user: false
5};
Use labeled tuples for clarity in return types
Label tuple elements to improve clarity when returning multiple values from a function.
function useFeature(): [boolean, () => void] { ... }
function useFeature(): [enabled: boolean, toggle: () => void] { ... }