Modela los estados de tu aplicación con uniones que el compilador comprueba por ti.
Abrir esta lección en KodokonUn tipo literal restringe un valor a un contenido exacto: 'info' en lugar de string. Es la base de las API imposibles de usar mal: el compilador rechaza cualquier valor fuera de la lista y tu editor ofrece autocompletado.
type Level = "info" | "warn" | "error";
function log(level: Level, message: string): void {
console.log(`[${level}] ${message}`);
}
log("info", "Server started");
// log("fatal", "Boom"); -> rejected by the compilerEl siguiente paso es combinar tipos de objeto que comparten una propiedad literal común, llamada el discriminante. Cada estado de una llamada de red, por ejemplo, lleva solo los datos que le corresponden: acceder a data durante la carga es sencillamente imposible.
type ApiState =
| { status: "loading" }
| { status: "success"; data: string[] }
| { status: "error"; message: string };
function render(state: ApiState): string {
switch (state.status) {
case "loading":
return "Loading...";
case "success":
return state.data.join(", ");
case "error":
return state.message;
}
}En cada case, TypeScript acota el tipo: después de case 'error', state.message existe y state.data no. Este mecanismo, llamado narrowing (acotamiento), reemplaza las comprobaciones manuales frágiles. Añade una guardia de exhaustividad para blindarlo todo.
function assertNever(value: never): never {
throw new Error("Unhandled case: " + String(value));
}
function label(state: ApiState): string {
switch (state.status) {
case "loading": return "In progress";
case "success": return "Done";
case "error": return "Failed";
default: return assertNever(state);
}
}