TypeScript Tips and Tricks for Better Type Safety
TypeScript is more than just adding types to JavaScript. When used effectively, it catches bugs at compile time, improves code documentation, and makes refactoring safer. Here are some advanced tips to take your TypeScript skills to the next level.
Use Discriminated Unions
Create type-safe state machines:
type Result<T> =
| { status: 'loading' }
| { status: 'error'; error: Error }
| { status: 'success'; data: T };
function handleResult<T>(result: Result<T>) {
switch (result.status) {
case 'loading':
return 'Loading...';
case 'error':
return `Error: ${result.error.message}`;
case 'success':
return result.data; // TypeScript knows data exists here
}
}
Leverage Utility Types
TypeScript includes powerful built-in utility types:
interface User {
id: string;
name: string;
email: string;
age: number;
}
// Make all properties optional
type PartialUser = Partial<User>;
// Make all properties required
type RequiredUser = Required<User>;
// Pick specific properties
type UserPreview = Pick<User, 'id' | 'name'>;
// Omit specific properties
type UserWithoutId = Omit<User, 'id'>;
// Make properties readonly
type ReadonlyUser = Readonly<User>;
Use Template Literal Types
Create precise string types:
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Route = '/users' | '/posts' | '/comments';
type Endpoint = `${HTTPMethod} ${Route}`;
// Endpoint is now:
// 'GET /users' | 'GET /posts' | 'GET /comments' |
// 'POST /users' | 'POST /posts' | ...
Const Assertions
Lock in literal types:
// Without const assertion
const colors = ['red', 'green', 'blue'];
// type: string[]
// With const assertion
const colors = ['red', 'green', 'blue'] as const;
// type: readonly ["red", "green", "blue"]
type Color = typeof colors[number];
// type: "red" | "green" | "blue"
Type Guards
Create custom type checking functions:
interface Dog {
bark(): void;
}
interface Cat {
meow(): void;
}
function isDog(animal: Dog | Cat): animal is Dog {
return (animal as Dog).bark !== undefined;
}
function handlePet(pet: Dog | Cat) {
if (isDog(pet)) {
pet.bark(); // TypeScript knows it's a Dog
} else {
pet.meow(); // TypeScript knows it's a Cat
}
}
Branded Types
Create nominal types in a structural type system:
type UserId = string & { readonly brand: unique symbol };
type PostId = string & { readonly brand: unique symbol };
function createUserId(id: string): UserId {
return id as UserId;
}
function getUser(id: UserId) {
// ...
}
const userId = createUserId('123');
const postId = '456' as PostId;
getUser(userId); // ✓ OK
getUser(postId); // ✗ Error: Type 'PostId' is not assignable to 'UserId'
Conditional Types
Create types that depend on other types:
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// More practical example
type Flatten<T> = T extends Array<infer U> ? U : T;
type A = Flatten<string[]>; // string
type B = Flatten<number>; // number
Mapped Types
Transform existing types:
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
interface User {
name: string;
age: number;
}
type NullableUser = Nullable<User>;
// {
// name: string | null;
// age: number | null;
// }
Use satisfies Operator
Ensure a value matches a type without widening:
type Colors = 'red' | 'green' | 'blue';
const colors = {
primary: 'red',
secondary: 'green',
} satisfies Record<string, Colors>;
// colors.primary is still 'red', not Colors
const p: 'red' = colors.primary; // ✓ OK
Strict Configuration
Enable strict mode in tsconfig.json:
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
}
}
Type-Safe Event Emitters
Create fully typed event systems:
type Events = {
'user:login': (user: User) => void;
'user:logout': () => void;
'post:create': (post: Post) => void;
};
class TypedEmitter {
on<K extends keyof Events>(event: K, callback: Events[K]) {
// ...
}
emit<K extends keyof Events>(
event: K,
...args: Parameters<Events[K]>
) {
// ...
}
}
Keep Learning
TypeScript is constantly evolving. Stay updated with:
Need TypeScript expertise on your project? Let's talk.