For JavaScript developers wishing to improve the development experience and include type safety into their applications, TypeScript has grown in popularity. Whether you're a seasoned developer or new to TypeScript, there are constantly new tricks and best practices to pick up. To help you get the most of TypeScript, below are some of the greatest hints and techniques.
1. Leverage Type Inference
TypeScript can automatically infer types based on your code, reducing the need for explicit type annotations.
let message = "Hello, TypeScript!"; // TypeScript infers the type as string
However, for function return types, it’s often a good practice to explicitly define them.
function greet(name: string): string {
return `Hello, ${name}!`;
}
2. Use Union and Intersection Types
Union and intersection types allow for more flexible type definitions.
type User = {
name: string;
age: number;
};
type Admin = {
admin: boolean;
};
type AdminUser = User & Admin; // Intersection type
type Response = User | Admin; // Union type
3. Type Guards
Type guards help you work with union types by narrowing down the type within a conditional block.
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ('swim' in animal) {
animal.swim();
} else {
animal.fly();
}
}
4. Use unknown Instead of any
Using unknown forces you to perform type checks before using the value, making your code safer.
let value: unknown;
value = "Hello";
value = 42;
if (typeof value === "string") {
console.log(value.toUpperCase());
}
5. Avoid Type Assertions When Possible
Type assertions should be used sparingly. Instead, let TypeScript infer the types or use type guards.
// Instead of this
const input = document.querySelector('input') as HTMLInputElement;
input.value = 'Hello';
// Prefer this
const input = document.querySelector('input');
if (input instanceof HTMLInputElement) {
input.value = 'Hello';
}
6. Leverage Utility Types
TypeScript provides several utility types that can simplify type transformations.
type User = {
name: string;
age: number;
address?: string;
};
type PartialUser = Partial<User>;
type ReadonlyUser = Readonly<User>;
type RequiredUser = Required<User>;
type PickUser = Pick<User, 'name' | 'age'>;
type OmitUser = Omit<User, 'address'>;
7. Strict Null Checks
Enable strictNullChecks in your tsconfig.json to prevent null or undefined values where they are not expected.
{
"compilerOptions": {
"strictNullChecks": true
}
}
8. Consistent Type Definitions
Use interfaces and type aliases consistently. Generally, interfaces are better for defining object shapes, and type aliases are better for other types such as unions and intersections.
interface User {
name: string;
age: number;
}
type Response = User | Error;
9. Use Enums for Fixed Sets of Values
Enums can make your code more readable and maintainable by providing a set of named constants.
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT"
}
function move(direction: Direction) {
console.log(`Moving ${direction}`);
}
move(Direction.Up);
10. Generics for Reusable Components
Generics allow you to create reusable components and functions with type safety.
function identity<T>(value: T): T {
return value;
}
const number = identity<number>(42);
const word = identity<string>("hello");
11. Type Narrowing with never
The never type represents values that never occur and can help with exhaustive checks in switch statements or similar constructs.
type Shape = { kind: "circle", radius: number } | { kind: "square", side: number };
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.side ** 2;
default:
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}
12. Make Use of readonly
Use readonly to prevent modification of properties.
type User = {
readonly name: string;
age: number;
};
const user: User = { name: "Alice", age: 30 };
// user.name = "Bob"; // Error: Cannot assign to 'name' because it is a read-only property.
user.age = 31; // OK
Conclusion
TypeScript is a powerful tool that can greatly enhance your JavaScript development experience by providing static type checking and many advanced features. By leveraging TypeScript’s type inference, utility types, strict null checks, and more, you can write safer, more readable, and maintainable code. These tips and tricks should help you make the most of TypeScript in your projects. Happy coding!
That's all folks 👋🏻
Top comments (1)
Hi Wallace Freitas,
Top, very nice and helpful !
Thanks for sharing.