10 Advanced TypeScript Tricks for Experienced Developers:
- **Conditional Types:** Shape types based on conditions, creating dynamic type hierarchies. For example, defining a type that’s an `object` with specific properties depending on a provided string value.
type Config = {
type: 'basic' | 'advanced';
// ... other properties
};
type BasicConfig = Config & { type: 'basic' };
type AdvancedConfig = Config & { type: 'advanced' };
function configure(config: Config): void {
if (config.type === 'basic') {
console.log('Applying basic configuration');
// config is now of type BasicConfig
} else {
console.log('Applying advanced configuration');
// config is now of type AdvancedConfig
}
}
2. **Mapped Types:** Transform existing types by applying operations to each property. Useful for adding prefixes/suffixes to properties, creating key-value pairs from objects, etc.
interface User {
name: string;
age: number;
}
type ReadOnly<T> = { readonly [P in keyof T]: T[P] };
const readOnlyUser: ReadOnly<User> = {
name: 'John Doe',
age: 30
};
// readOnlyUser.name = 'Jane Doe'; // Error: Cannot assign to 'name' because it is a read-only property
3. **Template Literal Types:** Build complex string types using dynamic expressions within backticks. Ideal for representing API responses, URL patterns, or validation formats.
type Endpoint = `/users/${number}`;
const validEndpoint: Endpoint = '/users/123';
// const invalidEndpoint: Endpoint = '/posts/456'; // Error: Type '"/posts/456"' is not assignable to type 'Endpoint'
4. **Higher-Order Types (HOTs):** Utilize functions that take and return types, enabling powerful abstractions like generic type predicates and type builders.
type IsStringLiteral<T> = T extends string ? true : false;
function logIfString<T>(value: T): T extends string ? void : T {
if (IsStringLiteral<T>) {
console.log(value);
}
return value;
}
5. **Recursive Types:** Define types that reference themselves, allowing creation of complex data structures like linked lists or trees.
interface LinkedList<T> {
value: T;
next?: LinkedList<T>;
}
const numbersList: LinkedList<number> = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: null
}
}
};
6. **Type Aliases:** Create reusable names for complex types, improving code readability and maintainability.
type UserResponse = {
id: number;
username: string;
email: string;
};
function fetchUser(): UserResponse {
// ...
}
7. **Unions and Intersections:** Combine multiple types using `|` and `&` operators, representing values that can be one or both types simultaneously.
type NumberOrString = number | string;
const value: NumberOrString = 123; // or value = "hello"
type Point = { x: number, y: number };
type LabeledPoint = Point & { label: string };
8. **Type Guards:** Narrow down the type of a variable within conditional statements using assertions like `typeof` or custom functions.
function isNumber(value: NumberOrString): value is number {
return typeof value === 'number';
}
if (isNumber(value)) {
console.log('Value is a number:', value);
} else {
console.log('Value is a string:', value);
}
9. **Decorators:** Extend the behavior of classes, properties, and methods with custom logic, enabling features like dependency injection, validation, or performance optimization.
function Log(target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${key}()`);
return originalMethod.apply(this, args);
};
}
class MyClass {
@Log
greet() {
console.log('Hello!');
}
}
10. **Utility Types:** Utilize built-in and community-defined utilities like `Pick`, `Omit`, or `Partial` to manipulate the structure of existing types without manual effort.
// Pick a subset of properties
type Address = Pick<User
**Bonus:** Explore advanced features like `keyof` operator, discriminated unions, generics with conditional types, and advanced generics patterns.
**Remember:** Always use advanced features for clear code, not just complexity. Balance power with readability and maintainability.
I hope these tricks expand your TypeScript toolkit and inspire you to write even more robust and expressive code!