Mejores prácticas en la programación con TypeScript
Este artículo explica las mejores prácticas en la programación con TypeScript.
Esta guía explica prácticas recomendadas para aprovechar los tipos de TypeScript y reducir errores, así como para escribir código más legible.
YouTube Video
Mejores prácticas en la programación con TypeScript
El mayor beneficio de TypeScript es 'prevenir errores con los tipos y dejar claro la intención del código'.
Las mejores prácticas no son simplemente reglas, sino un conjunto de principios para escribir código seguro, legible y mantenible. A continuación, presentamos las mejores prácticas de TypeScript con ejemplos prácticos.
Evite any y siempre defina los tipos de manera significativa
Primero, veamos el punto de 'evitar any y asignar tipos significativos'.
any desactiva completamente la comprobación de tipos, lo que contradice el propósito de usar TypeScript. En vez de usar any solo para que funcione, es importante proporcionar tipos lo más descriptivos posible.
1// Bad
2function parse(data: any) {
3 return data.value;
4}Este código puede aceptar cualquier valor, por lo que no se pueden prevenir errores en tiempo de ejecución.
1// Good
2type ParsedData = {
3 value: string;
4};
5
6function parse(data: ParsedData): string {
7 return data.value;
8}Al definir los tipos, clarifica la intención de las entradas y salidas, y mejora la seguridad.
Defina siempre explícitamente las estructuras de objetos usando type o interface.
A continuación, veamos el punto de 'definir siempre explícitamente las estructuras de los objetos usando type o interface'.
Si utiliza objetos ad-hoc, su estructura puede volverse ambigua. Siempre extraiga los tipos para reutilización y mantenibilidad.
1// Bad
2function createUser(user: { name: string; age: number }) {
3 console.log(user.name);
4}Incluso en fragmentos pequeños de código, es importante desarrollar el hábito de separar los tipos.
1// Good
2type User = {
3 name: string;
4 age: number;
5};
6
7function createUser(user: User): void {
8 console.log(user.name);
9}Al nombrar los tipos, comprender la base de código se vuelve mucho más sencillo.
Use tipos unión para representar con precisión todos los posibles estados.
Si utiliza tipos string o number sin restricciones en condicionales, pueden pasar valores inesperados. Al usar tipos de unión, puedes representar solo los estados permitidos a nivel de tipo.
1// Bad
2function setStatus(status: string) {
3 console.log(status);
4}En este código, las cadenas sin sentido o los valores incorrectos no pueden ser detectados en tiempo de compilación.
1// Good
2type Status = "idle" | "loading" | "success" | "error";
3
4function setStatus(status: Status): void {
5 console.log(status);
6}Al usar tipos de unión, puedes eliminar de forma fiable los 'estados imposibles' en tiempo de compilación. Como resultado, la seguridad de las ramas condicionales y la confiabilidad del código mejorarán.
Gestione explícitamente null y undefined.
Ahora, veamos el punto de 'manejar explícitamente null y undefined'.
En TypeScript, es importante expresar la posible ausencia de un valor en el tipo. Si mantiene las cosas ambiguas, pueden producirse errores en tiempo de ejecución.
1type User = {
2 name: string;
3 email?: string;
4};email puede no existir, por lo que debe manejarlo con esa suposición.
1function printEmail(user: User): void {
2 if (user.email) {
3 console.log(user.email);
4 }
5}Revise siempre los valores opcionales antes de usarlos.
Utilice aserciones de tipos (as) solo como último recurso.
Ahora, veamos el punto de 'no abusar de las aserciones de tipo'.
Las aserciones de tipo omiten temporalmente la comprobación de tipos de TypeScript para declarar: 'Sé que este valor es de este tipo.'. El uso excesivo de ellas debilita la seguridad de tipos.
1// Bad
2const value = input as string;En este código, aunque el valor real no sea una cadena, no ocurre ningún error, lo que puede provocar errores en tiempo de ejecución. Como se muestra en el siguiente código, es mejor elegir primero la verificación segura utilizando type guards.
1// Good
2function isString(value: unknown): value is string {
3 return typeof value === "string";
4}
5
6if (isString(input)) {
7 console.log(input.toUpperCase());
8}Los type guards son un mecanismo para determinar un tipo de forma segura mientras se verifica el valor real. Al priorizar los type guards sobre las aserciones de tipo, puedes prevenir más fácilmente los errores en tiempo de ejecución.
No dependa demasiado de la inferencia de tipos para los tipos de retorno.
Ahora, veamos el punto de 'no depender demasiado de la inferencia para los tipos de retorno'.
La inferencia de tipos de TypeScript es potente, pero es más seguro anotar explícitamente los tipos de retorno de funciones públicas. Esto minimiza el posible impacto de cambios futuros.
1// Bad
2function sum(a: number, b: number) {
3 return a + b;
4}Exprese claramente su intención incluso en funciones pequeñas.
1// Good
2function sum(a: number, b: number): number {
3 return a + b;
4}Especificar los tipos de retorno aumenta la estabilidad de su API.
Gestione las entradas de manera segura utilizando unknown.
Ahora, veamos el punto de 'aceptar de manera segura entradas externas usando unknown'.
Para entradas externas como APIs, JSON o entradas de usuario, usa unknown en lugar de any. De este modo, aseguras que todos los valores sean validados, manteniendo la seguridad de tipos.
1// Bad
2function handleResponse(data: any) {
3 console.log(data.id);
4}Aquí se muestra cómo validar tipos con unknown.
1// Good
2function handleResponse(data: unknown): void {
3 if (
4 typeof data === "object" &&
5 data !== null &&
6 "id" in data
7 ) {
8 console.log((data as { id: number }).id);
9 }
10}unknown no puede ser usado tal cual; es un tipo que requiere validación. Es especialmente efectivo al manejar entradas externas.
Aumente la expresividad combinando tipos pequeños.
Ahora, veamos el punto de 'aumentar la expresividad combinando tipos pequeños'.
Definir tipos grandes de una sola vez reduce la legibilidad y la mantenibilidad. Divida sus tipos en unidades significativas y combínelos según sea necesario.
1type Id = number;
2
3type UserProfile = {
4 id: Id;
5 name: string;
6};
7
8type UserWithStatus = UserProfile & {
9 status: "active" | "inactive";
10};Tratar los tipos como componentes ayuda a organizar su diseño.
type e interface
Ventajas de interface
type e interface pueden definir tipos, pero sus usos y características previstas difieren. Al usarlos para los roles apropiados, la intención de sus definiciones de tipos queda más clara.
1// Bad
2type User = {
3 id: number;
4 name: string;
5};
6
7type AdminUser = {
8 id: number;
9 name: string;
10 role: "admin";
11};Si duplica partes comunes de esta manera, su código se vuelve frágil ante cambios.
1// Good
2interface User {
3 id: number;
4 name: string;
5}
6
7interface AdminUser extends User {
8 role: "admin";
9}interface es ideal para diseños que impliquen extensiones (extends) y es más adecuado para expresar la 'forma' de los objetos.
Ventajas de type
Por otro lado, type es más expresivo y adecuado para manejar tipos unión e intersección.
1// Good
2type Status = "idle" | "loading" | "success" | "error";
3
4type ApiResponse<T> =
5 | { status: "success"; data: T }
6 | { status: "error"; message: string };type es especialmente adecuado para expresar estados, opciones y combinaciones.
Guía para elegir entre type e interface
Como regla general, use interface para estructuras y contratos de objetos, y type cuando necesite la expresividad de unión, intersección u operaciones de tipos.
Ambos funcionan de manera similar, pero es importante elegir según lo que comunique la razón de existencia del tipo.
Considere sus tipos como documentación.
Finalmente, veamos el punto de 'escribir los tipos como documentación'.
Buenas definiciones de tipos transmiten más información que los comentarios. Es importante aspirar a un estado en el que 'la especificación pueda entenderse solo con mirar los tipos'.
1type ApiError = {
2 code: number;
3 message: string;
4 retryable: boolean;
5};De este modo, una de las grandes fortalezas de TypeScript es que las definiciones de tipos pueden funcionar como una especie de documento de especificación.
Resumen
Las mejores prácticas en TypeScript no se tratan de ser demasiado estrictos. La esencia es clarificar la intención mediante los tipos y escribir código robusto frente a cambios.
Al acumular pequeñas reglas en el desarrollo diario, puede lograr efectos a largo plazo como 'revisiones más fáciles', 'menos errores' y 'mejor comprensión para usted mismo en el futuro y para otros.'.
Primero, al pensar '¿cómo puedo expresar esto con tipos?', logrará escribir código de alta calidad al estilo TypeScript.
Puedes seguir el artículo anterior utilizando Visual Studio Code en nuestro canal de YouTube. Por favor, también revisa nuestro canal de YouTube.