Fecha publicación: 25 sept 2021

TypeScript Anotaciones

¿Cuantas veces te ha pasado que no sabías bien que tipo tenía una variable y has introducido lo que no era? Seguro que en esos momentos has echado de menos el sistema de tipos que traen lenguajes como C# o Java...

En este video vamos a arrancarnos con lo más básico de TypeScript: las anotaciones de tipo.

Manos a la obra

Conceptos y tipos primitivos

Para anotar tipos con TypeScript vamos a usar los dos puntos como si de el valor de una propiedad se tratase, es decir para tipar una variable vamos a añadir su anotación de tipo a continuación de la variable.

por ejemplo supongamos que tenemos la siguiente variable city que va a ser un string:

const city;

Para tiparla le añado los dos puntos y le añado el tipo string fijaros en en el IDE (editor de código) me lo pone de color verde.

const city: string;

Y a continuación le podemos asignar el valor que necesitemos, por ejemplo la cadena de texto Málaga.

const city: string = "Málaga";

¿Cual es el propósito de la anotación de tipos? Pues generar código robusto, básicamente, asegurarnos de que los tipos se cumplen y se respetan de manera que si yo pretendo modificar este valor y en lugar de un string intento meter un número, me lo detecte y fme avise del error, fijaros que sucede si introduzco el número 4 en vez de la cadena de texto anterior: aparece un advertencia en rojo diciendo que el 4 no es un tipo asignable a string.

// Error...
const city: string = 4;

Esto es posible porque en mi entorno dispongo de un linter, un linter es una especia de plugin que va a ir ejecutando el chequeo del tipos de Typescript en vivo de modo que a medida que voy desarrollando me va advirtiendo de posibles fallos en el tipado.

Además si lanzáramos una transpilación de este código Typescript este error volvería a saltar, nos daría la advertencia que se ha produccido un error no solo en tiempo de desarrollo si no también en tiempo de transpilación.

Además TypeScript no sólo incorpora los errores propios del tipado si no también del lenguaje, por ejemplo si yo intento reasignar esta variable que hemos puesto que es constante, fijaos que me va a avisar igualmente de que no se puede asignar porque es una constante.

const city: string = "Málaga";
// Error intenta asignar un const
city = "Madrid";

Por tanto como resumen: si queremos generar código robusto a nivel de tipado nos va a aportar mucho valor utilizar TypeScript para anotar los tipos activamente en mi código como hemos hecho aquí, si además de esto disponemos de un linter que va haciendo el chequeo de tipos de forma silenciosa, tirando de TypeScript podemos mejorar de forma considerable nuestra productividad.

Veamos cuales son los tipos básicos que nos ofrece TypeScript, los tres tipos primitivos fundamentales son sencillos:

// Boolean
const fake: boolean = true;

// Number
const integer: number = 13;
const float: number = 13.13;
const exp: number = 1e13;

// String
const text: string = "Hello World !";

Los valores Booleanos (true/false), los anotamos como boolean.

Los valores numéricos, los anotamos como number.

Las cadentas de texsto como string.

Fijate que todos ellos los escribimos en minúsculas.

Arrays

Vamos a ver como tipar arrays, tomamos este punto de partida (no tipado):

const coins = [1, 2, 0.5];
const letters = ["a", "b", "c"];

El primero es un array de números, el segundo es un array strings.

¿Cómo haríamos este tipado? Pues primero le indicamos el tipo de cada elemento, en el primer caso number y después le abro y cierro corchetes para indicarle que es un array.

Para el caso del array de strings seguimos la misma aproximación, pero indicándole que el tipo de cada elemento es string.

const coins: number[] = [1, 2, 0.5];
const letters: string[] = ["a", "b", "c"];

Existe una alternativa que todavía no hemos visto que se llaman genéricos (esto lo veremos en más detalle en siguientes capítulos), consiste en escribir la palabra Array y entre angulitos el tipo de los elementos de mi array, el código que viene a continuación es equivalente al anterior:

const coins: Array<number> = [1, 2, 0.5];
const letters: Array<string> = ["a", "b", "c"];

¿Que sucedería si la naturaleza de mi array es heterogénea? Es decir el array contiene elementos de tipos diferentes, por ejemplo

const quantity = [5, "pieces"];

A esto se le conoce como tupla y también se puede, en este caso lo que haremos es abrimos y cerramos los corchetes y ponemos los tipos uno a uno de los elementos que tenemos a la derecha, en este caso sería un number y un string:

const quantity: [number, string] = [5, "pieces"];

Enumerados

En TypeScript contamos con un nuevo tipo: el enumerado (este nos permite mapear valores números o de otro tipo a un alias), para crear un enumerado lo que vamos a hacer es escribir la palabra enum y a continuación le damos el nombre que nosotros queramos, por ejemplo vamos a escribir por ejemplo los días de la semana, llamaremos a este enumerado DiaSemana, y a continuación enumero la lista de valores, ¿Cómo hago esto? abro y cierro llaves y pongo las opciones (en este caso lunes, martes, miércoles...)

enum DiaSemana {
  Lunes, //=0
  Martes, //=1
  Miercoles, //=2
  Jueves, //=3
  Viernes, //=4
  Sábado, //=5
  Domingo, //=6
}

¿Que es por tanto un enumerado? un enumerado es básicamente un alias más amigable para tipos numéricos, es decir, es una manera de ponerle un alias (lunes, martes...) a numeros, en este caso el lunes representa el número 0, el martes el numero 1..., de esta forma yo lo que he hecho es definir cuales van a ser los alias. ¿Cómo podemos usarlo?: vamos a crear un constante, que se llamara dia y le decimos que su tipo es el tipo enumerado DiaSemana ¿Cómo le puedo asignar valores? directamente le pongo DiaSemana y vemos como intellisense lo reconoce, le doy a la tecla para mostrar un punto y ahí veo todas las opciones posibles (por ejemplo DiaSemana.Lunes).

El enum dia de la semana aparece cuando se nos muestra el intellisense de VSCode

const dia: diaSemana = DiaSemana.Lunes;

Y si mostramos esto por consola vemos que el contenido de día es precisamente un número (el cero), puesto que hemos definido DiaSemana.Lunes como un alias para el cero.

const dia: diaSemana = DiaSemana.Lunes;
console.log(dia)=

Al hacer console log del día sale un cero

Estos números me los asigna TypeScript de manera automática, es decir, por defecto, la primera opción de nuestros enumerados siempre va a tener el cero cómo valor, la segunda el uno así sucesivamente, nosotros podemos alterar ese orden, podemos decirle, por ejemplo, que arranque en 1:

enum DiaSemana {
+  Lunes = 1, // 1
-  Lunes, //=0
  Martes, //=2
  Miercoles, //=3
  Jueves, //=4
  Viernes, //=5
  Sábado, //=6
  Domingo, //=7
}

Vamos a suponer que quiero que el domingo quiero que sea el primer día de la semana (cómo en el calendario USA), le podemos indicar que el Domingo tenga el valor cero:

enum DiaSemana {
+  Lunes = 1, // 1
-  Lunes,
  Martes,
  Miercoles,
  Jueves,
  Viernes,
  Sábado,
+  Domingo = 0,
-  Domingo,
}

Y ahora si ejecutamos este código, podemos comprobar que el lunes tiene el valor 1.

Al hacer console log del día sale un uno

Para terminar con los enumerados, indicar que además de números podemos tener por ejemplo strings, fijaros en la pequeña modificación que he hecho para el ejemplo anterior: ahora cada una de las opciones van a ser abreviaturas para los días de la semana, y lo que están representando sirven de alias a strings en lugar de a números, así pues estamos mapeando "L" a "Lunes", "M" a "Martes" y así sucesivamente.

enum DiaSemana {
  L = "Lunes",
  M = "Martes",
  X = "Miercoles",
  J = "Jueves",
  V = "Viernes",
  S = "Sábado",
  D = "Domingo",
}

const dia: DiaSemana = DiaSemana.L;
console.log(dia);

si lanzamos la ejecución de este código, dia contiene el string lunes, aunque en nuestro código lo tengamos representado como una L

La L se mapea ahora a lunes

Cerramos este apartado, recordando que para definir tipos que no sean primitivos, se suele utilizar la convención Pascal Case, es decir primera letra de palabra siempre en mayúscula, por ejemplo el tipo DiaSemana tiene la primera D en mayúscula y la S de inicio de la segunda palabra también.

Null, undefined y any

Retomamos los tipos primitivos, vamos con dos un poco "especiales": null y undefined en este caso podemos definir una variable y a continuación con uno de esos dos valores.

const u: undefined = undefined; // Sólo se le puede asignar undefined o null
const n: null = null;

Hasta aquí parece que todo bien pero... espera un momento: fíjate en el comentario indicamos que a const u : undefined le puedo asignar también null ¿Comooor? Si, por defecto TypeScript permite que una variable de cualquier tipo pueda admitir como valores válidos null o undefined, es lo que coloquialmente llamamos tipos comodines.

Por ejemplo podemos asignar a una variable de tipo number un valor null o undefined:

const num: number = null;

Para el tipado de objetos tenemos disponible en TypeScript la palabra Object y esto nos permite tipar nuestras variables como objeto.

const o: object = {};

Pero vamos a obviar este tipo porque es excesivamente vago, es decir demasiado amplio, representa o tipa cualquier tipo de objeto posible con cualquier propiedad y esto no siempre es lo deseable, nosotros necesitamos poder ser mucho más estrictos a la hora de tipa objetos e indicar que deben tener ciertas propiedades y esas propiedades cumplan con cierto tipado, etc..., por lo tanto vamos a evitar utilizar en la medida de lo posible el tipo object. En próximos capítulos veremos como tipar un objeto de una manera más precisa utilizando los interface.

Veamos ahora un tipo de utilidad que ofrece Typescript que son void y never, lo vamos a ver en el contexto de las funciones, en este caso vemos la función sayHi que muestra un texto por consola pero no devuelve nada en la función, cuando no hace retorno es aconsejable indicarle con el tipo void que esta función no devuelve ningún valor:

functionm sayHi() : void {
  console.log("Hi");
}

Si ahora intento devolver un valor, el sistema de tipado me dirá que no puedo hacerlo.

functionm sayHi() : void {
  console.log("Hi");
  return true; // Error
}

Never representa valores que nunca van a ocurrir, es un tipo que raramente lo vas a utlizar, se suele usar mucho a nivel interno en Typescript para indicar situaciones con tipos complejos, composiciones de tipo que no deberían suceder... Como ejemplo sencillo, veamos un bucle infinito intencionado y vamos a indicarle que la función que lo ejecuta jamás devolver ningún valor, es decir le indicaremos que el retorno es never.

function inifiniteloop(): never {
  while (true) {}
}

De esta forma si hacemos un return nos daría un error

function inifiniteloop(): never {
  while (true) {}
  return true; // Error
}

Para terminar con este capítulo, vamos a ver el tipo any.Este tipo representa cualquier tipo posible, supongamos la siguiente situación: tenemos una función getserverdata cuya implementación desconocemos, no esta tipada, y no se que me devuelve (podría por ejemplo leer datos de una API Rest de servidor),no se si me devuelve un number, un string, un boolean... no tenemos ni idea como puedo tipar esta variable.

const getServerData = () => {
  /*¿?*/
};
let myData = getServerData();

La única manera que nos queda aquí es poner :any

const getServerData = () => {
  /*¿?*/
};
- let myData: any = getServerData();
+ let myData: any = getServerData();

Fijaos que al poner un any podría ser un número, un string... cualquier cosa

const getServerData = () => {
  /*¿?*/
};
let myData: any = getServerData();
myData = 4;
myData = "hola";
myData = true;

El sistema de tipos me lo permite, todo esto es posible porque es un any, ¿Qué inconveniente que tiene esto? que es peligroso, any es muy potente pero abre demasiado el abanico de posibilidades, digamos que desactivamos el chequeo de tipos, en el momento en el que metemos un any en nuestro código el chequeo de tipos abre la puerta a cualquier tipo, cualquier valor, esto puede ser una fuente de errores considerable, es lo más parecido a volver a JavaScript y olvidarnos de TypeScript, hay que evitar en la medida de lo posible el uso del any.

Vamos a quedarnos con el ejemplo anterior para explicar un último detalle que es la aseveración de tipos (type assertion), supongamos el ejemplo de antes en que hemos ido a servidor hemos traído datos pero no sabemos que tipo tiene el valor que obtenemos, hasta aquí todo igual, pero hablamos con uno de los chicos de back y nos comenta no nos preocupemos que ese valore va a ser un string ¿Qué podemos hacer? Una especia de casting,una conversión, ya que si yo intento utilizar myData como si fuese un string, no voy a recibir ayuda del intellisense ya que está definido como any.

Conversiones al rescate...

const getServerData = () => { /*¿?*/}
let myData  : any = getServerData();

+ console.log((myData as string).substr(0));

Así le decimos a TypeScript que tenga fe y que crea que myData es realmente un string, de esta manera ya intellisense me detecta que tengo un string y muestra todas las propiedades que tiene este tipo.

Otra alternativa es usar el as, podemos poner el tipo delante y lo envolverlo entre angulitos, esto sería un genérico (que veremos más adelante).

const getServerData = () => { /*¿?*/}
let myData  : any = getServerData();

console.log((myData as string).substr(0));
+ console.log((<string>myData).substr(1));

Si completamos la función devolviendo un string, podemos ejecutar y ver por consola que se muestra el resultado esperado.

¿Te apuntas a nuestro máster?

Si te ha gustado este ejemplo y tienes ganas de aprender Front End guiado por un grupo de profesionales ¿Por qué no te apuntas a nuestro Máster Front End Online Lemoncode? Tenemos tanto edición de convocatoria con clases en vivo, como edición continua con mentorización, para que puedas ir a tu ritmo y aprender mucho.