Publicado · en mejora
Guía de TypeScript · 6/6
Por ahora, este capítulo solo está disponible en inglés.
TypeScript's real power lives in its type system. This chapter digs into generics for reusable types, narrowing and type guards that follow your control flow, discriminated unions for modeling state safely, utility types for transforming existing types, and the strict mode settings that make all of it trustworthy.
Generics let a function or type take a type as a parameter, so it can work with many types without forgetting which one it was given. Constrain a type parameter with extends, and combine it with keyof to tie an object's keys to their value types.
function first<T>(items: T[]): T | undefined {
return items[0];
}
const n = first([1, 2, 3]); // number | undefined
const s = first(["a", "b"]); // string | undefined
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length >= b.length ? a : b;
}
longest("hello", "hi"); // string
longest([1, 2], [1, 2, 3]); // number[]
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: "Kim", age: 30 };
const age = getProp(user, "age"); // number
// getProp(user, "email"); // error: not a key of user
interface ApiResponse<T> {
data: T;
status: number;
}With any you could accept anything, but the return type would be lost. Generics keep both flexibility and safety.
A value with a union type gets refined to something more specific as it passes through checks. The compiler understands typeof, instanceof, the in operator, equality comparisons, and truthiness checks. To turn your own checking function into a type guard, give it a type predicate of the form value is Type as its return type.
interface Cat { meow(): void }
interface Dog { bark(): void }
function speak(pet: Cat | Dog) {
if ("meow" in pet) pet.meow(); // Cat
else pet.bark(); // Dog
}
function isString(value: unknown): value is string {
return typeof value === "string";
}
function shout(input: unknown) {
if (isString(input)) {
console.log(input.toUpperCase()); // narrowed to string
}
}
function len(text: string | null): number {
if (!text) return 0; // filters out null and ""
return text.length; // string
}A discriminated union is a union whose members share a property, usually called kind or type, holding a different literal value in each member. Switch on that property and every branch knows the exact member it is dealing with. Assigning to never in the default branch turns forgotten cases into compile errors when the union grows.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; size: number }
| { kind: "rect"; width: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2;
case "square": return shape.size ** 2;
case "rect": return shape.width * shape.height;
default: {
const unreachable: never = shape; // errors if a case is missing
return unreachable;
}
}
}This pattern shines for UI states such as loading, success, and failure, and for message types exchanged with a server.
TypeScript ships a set of built-in utility types that derive new types from existing ones.
| Utility | Effect |
|---|---|
Partial<T> | Makes every property optional |
Required<T> | Makes every property required |
Readonly<T> | Makes every property read-only |
Pick<T, K> and Omit<T, K> | Keep or drop selected properties |
Record<K, V> | An object with keys K and values V |
ReturnType<F> and Parameters<F> | A function's return and parameter types |
Awaited<T> | The value a Promise resolves to |
interface Todo {
id: number;
title: string;
done: boolean;
}
type TodoPatch = Partial<Omit<Todo, "id">>; // title?, done?
type TodoPreview = Pick<Todo, "id" | "title">;
type ByStatus = Record<"open" | "closed", Todo[]>;
function update(todo: Todo, patch: TodoPatch): Todo {
return { ...todo, ...patch };
}
const config = {
port: 3000,
mode: "dev",
} satisfies Record<string, string | number>;The satisfies operator at the end validates a value against a type while keeping the narrower inferred type, instead of widening the variable to the annotation.
"strict": true turns on a family of checks at once. The ones that matter most:
strictNullChecks treats null and undefined as distinct types, so you cannot use a possibly missing value without checking it.noImplicitAny stops parameters and variables from silently falling back to any.strictPropertyInitialization verifies that class properties are assigned in the constructor.useUnknownInCatchVariables types catch variables as unknown.Some useful checks live outside the strict family. noUncheckedIndexedAccess, for example, adds undefined to values read through array indexes or index signatures.
typeof, instanceof, in, and more; value is T creates custom guards.never check guarantee every case is handled.Partial, Pick, Omit, and Record reshape existing types.strict on, always.Where to go next in the official documentation:
0 comentarios
Iniciar sesión · Inicia sesión para dejar un comentario.
Sé el primero en comentar.