Advanced Types: Mastering the Type System
Once you understand the basics of TypeScript, you can leverage its more advanced features to handle complex logic, data transformations, and state management with extreme precision.
1. Type Narrowing and Type Guards
TypeScript is excellent at narrowing down a broad type (like string | number) into a specific one based on your logic.
typeof: Used for primitive types.instanceof: Used for class instances.- User-Defined Type Guards: Using the
iskeyword.
function isString(value: unknown): value is string {
return typeof value === "string";
}
function process(input: string | number) {
if (isString(input)) {
console.log(input.toUpperCase()); // TS knows input is string
}
}
2. Discriminated Unions
A Discriminated Union uses a common property (a "literal" or "tag") to distinguish between different types in a union. This is the gold standard for state management (e.g., Redux actions).
interface Success {
status: "success";
data: string[];
}
interface Failure {
status: "error";
message: string;
}
type ApiResponse = Success | Failure;
function handleResponse(response: ApiResponse) {
if (response.status === "success") {
console.log(response.data); // OK
} else {
console.log(response.message); // OK
}
}
3. Mapped Types and Utility Types
TypeScript provides several built-in "Utility Types" that allow you to transform one type into another.
Partial<T>: Makes all properties inToptional.Readonly<T>: Makes all properties inTread-only.Pick<T, K>: Creates a type by picking a set of propertiesKfromT.Omit<T, K>: Creates a type by removing propertiesKfromT.Record<K, T>: Creates an object type with keysKand valuesT.
interface Todo {
title: string;
description: string;
completed: boolean;
}
// Partial
const update: Partial<Todo> = { completed: true };
// Pick
type TodoPreview = Pick<Todo, "title" | "completed">;
4. Conditional Types
Conditional types allow you to select one of two types based on a condition expressed as a type relationship test.
type IsString<T> = T extends string ? "Yes" : "No";
type A = IsString<string>; // "Yes"
type B = IsString<number>; // "No"
5. Template Literal Types
Built on top of string literal types, these allow you to create complex string patterns.
type World = "world";
type Greeting = `hello ${World}`; // "hello world"
type Color = "red" | "blue";
type Intensity = "light" | "dark";
type Palette = `${Intensity}-${Color}`; // "light-red" | "light-blue" | "dark-red" | "dark-blue"
6. The keyof and typeof Operators
keyof: Takes an object type and produces a string or numeric literal union of its keys.typeof: Used in a type context to refer to the type of a variable or property.
const config = { width: 100, height: 200 };
type ConfigKeys = keyof typeof config; // "width" | "height"
7. Summary
- Type Guards make your logic safer by narrowing types.
- Discriminated Unions provide a clear way to handle multiple states.
- Utility Types save you from writing repetitive interface definitions.
- Mapped and Conditional Types allow for advanced API and library design.