Tipos Generales
Number, String, Boolean, Symbol y Object
❌ No uses nunca los tipos Number, String, Boolean, Symbol, o Object
Estos tipos se refieren a objetos primitivos con envoltura que casi nunca se usan apropiadamente en el código JavaScript.
ts/* INCORRECTO */function reverse(s: String): String;
✅ Sí usa los tipos number, string, boolean, y symbol.
ts/* CORRECTO */function reverse(s: string): string;
En lugar de Object, usa el tipo no primitivo object (agregado en TypeScript 2.2).
Genéricos
❌ No tengas nunca un tipo genérico que no use su parámetro de tipo. Consulta más detalles en la página de preguntas frecuentes de TypeScript.
any
❌ No uses any como tipo a menos que estés en proceso de migrar un proyecto JavaScript a TypeScript. El compilador efectivamente trata any como “por favor, desactiva la verificación de tipos para esto”. Es similar a poner un comentario @ts-ignore alrededor de cada uso de la variable. Esto puede ser muy útil cuando estás migrando por primera vez un proyecto JavaScript a TypeScript, ya que puedes establecer el tipo para las cosas que aún no has migrado como any, pero en un proyecto TypeScript completo estás deshabilitando la verificación de tipos para cualquier parte de tu programa que lo use.
En los casos en que no sepas qué tipo quieres aceptar, o cuando quieras aceptar cualquier cosa porque la pasarás ciegamente sin interactuar con ella, puedes usar unknown.
Tipos de Callback
Tipos de Retorno de Callbacks
❌ No uses el tipo de retorno any para callbacks cuyo valor será ignorado:
ts/* INCORRECTO */function fn(x: () => any) {x();}
✅ Sí usa el tipo de retorno void para callbacks cuyo valor será ignorado:
ts/* CORRECTO */function fn(x: () => void) {x();}
❔ Por qué: Usar void es más seguro porque te impide usar accidentalmente el valor de retorno de x de forma no verificada:
tsfunction fn(x: () => void) {var k = x(); // ¡ups! quería hacer otra cosak.doSomething(); // error, pero estaría bien si el tipo de retorno hubiera sido 'any'}
Parámetros Opcionales en Callbacks
❌ No uses parámetros opcionales en callbacks a menos que realmente lo necesites:
ts/* INCORRECTO */interface Fetcher {getObject(done: (data: unknown, elapsedTime?: number) => void): void;}
Esto tiene un significado muy específico: el callback done podría invocarse con 1 argumento o podría invocarse con 2 argumentos.
El autor probablemente pretendía decir que al callback podría no importarle el parámetro elapsedTime,
pero no hay necesidad de hacer el parámetro opcional para lograr esto —
siempre es legal proporcionar un callback que acepte menos argumentos.
✅ Sí escribe los parámetros del callback como no opcionales:
ts/* CORRECTO */interface Fetcher {getObject(done: (data: unknown, elapsedTime: number) => void): void;}
Sobrecargas y Callbacks
❌ No escribas sobrecargas separadas que difieran solo en la aridad del callback:
ts/* INCORRECTO */declare function beforeAll(action: () => void, timeout?: number): void;declare function beforeAll(action: (done: DoneFn) => void,timeout?: number): void;
✅ Sí escribe una sola sobrecarga usando la aridad máxima:
ts/* CORRECTO */declare function beforeAll(action: (done: DoneFn) => void,timeout?: number): void;
❔ Por qué: Siempre es legal que un callback ignore un parámetro, por lo que no hay necesidad de la sobrecarga más corta. Proporcionar primero un callback más corto permite pasar funciones con tipos incorrectos porque coinciden con la primera sobrecarga.
Sobrecargas de Funciones
Ordenación
❌ No pongas sobrecargas más generales antes que sobrecargas más específicas:
ts/* INCORRECTO */declare function fn(x: unknown): unknown;declare function fn(x: HTMLElement): number;declare function fn(x: HTMLDivElement): string;var myElem: HTMLDivElement;var x = fn(myElem); // x: unknown, ¿qué?
✅ Sí ordena las sobrecargas poniendo las firmas más generales después de las firmas más específicas:
ts/* CORRECTO */declare function fn(x: HTMLDivElement): string;declare function fn(x: HTMLElement): number;declare function fn(x: unknown): unknown;var myElem: HTMLDivElement;var x = fn(myElem); // x: string, :)
❔ Por qué: TypeScript elige la primera sobrecarga coincidente al resolver llamadas a funciones. Cuando una sobrecarga anterior es “más general” que una posterior, la posterior queda efectivamente oculta y no se puede llamar.
Usa Parámetros Opcionales
❌ No escribas varias sobrecargas que difieran solo en los parámetros finales:
ts/* INCORRECTO */interface Example {diff(one: string): number;diff(one: string, two: string): number;diff(one: string, two: string, three: boolean): number;}
✅ Sí usa parámetros opcionales siempre que sea posible:
ts/* CORRECTO */interface Example {diff(one: string, two?: string, three?: boolean): number;}
Ten en cuenta que esta simplificación solo debe ocurrir cuando todas las sobrecargas tienen el mismo tipo de retorno.
❔ Por qué: Esto es importante por dos razones.
TypeScript resuelve la compatibilidad de firmas verificando si alguna firma del destino puede invocarse con los argumentos de la fuente, y se permiten argumentos extraños. Este código, por ejemplo, expone un error solo cuando la firma se escribe correctamente usando parámetros opcionales:
tsfunction fn(x: (a: string, b: number, c: number) => void) {}var x: Example;// Cuando se escribe con sobrecargas, OK -- usa la primera sobrecarga// Cuando se escribe con opcionales, correctamente un errorfn(x.diff);
La segunda razón es cuando un consumidor utiliza la característica “strict null checking” de TypeScript.
Dado que los parámetros no especificados aparecen como undefined en JavaScript, normalmente está bien pasar un undefined explícito a una función con argumentos opcionales.
Este código, por ejemplo, debería estar bien bajo strict nulls:
tsvar x: Example;// Cuando se escribe con sobrecargas, incorrectamente un error debido a pasar 'undefined' a 'string'// Cuando se escribe con opcionales, correctamente OKx.diff("something", true ? undefined : "hour");
Usa Tipos Unión
❌ No escribas sobrecargas que difieran por tipo en una sola posición de argumento:
ts/* INCORRECTO */interface Moment {utcOffset(): number;utcOffset(b: number): Moment;utcOffset(b: string): Moment;}
✅ Sí usa tipos unión siempre que sea posible:
ts/* CORRECTO */interface Moment {utcOffset(): number;utcOffset(b: number | string): Moment;}
Ten en cuenta que no hicimos b opcional aquí porque los tipos de retorno de las firmas difieren.
❔ Por qué: Esto es importante para las personas que están “pasando” un valor a tu función:
tsfunction fn(x: string): Moment;function fn(x: number): Moment;function fn(x: number | string) {// Cuando se escribe con sobrecargas separadas, incorrectamente un error// Cuando se escribe con tipos unión, correctamente OKreturn moment().utcOffset(x);}