DEV Community

Wallace Freitas
Wallace Freitas

Posted on

Best Tips and Tricks for TypeScript

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
Enter fullscreen mode Exit fullscreen mode

However, for function return types, it’s often a good practice to explicitly define them.

function greet(name: string): string {
    return `Hello, ${name}!`;
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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());
}
Enter fullscreen mode Exit fullscreen mode

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';
}
Enter fullscreen mode Exit fullscreen mode

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'>;
Enter fullscreen mode Exit fullscreen mode

7. Strict Null Checks

Enable strictNullChecks in your tsconfig.json to prevent null or undefined values where they are not expected.

{
    "compilerOptions": {
        "strictNullChecks": true
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
jangelodev profile image
João Angelo

Hi Wallace Freitas,
Top, very nice and helpful !
Thanks for sharing.