Cubrimos las principales características del sistema de tipos de TypeScript cuando hablamos de ¿Por qué TypeScript? Los siguientes son algunos puntos clave de esa discusión que no necesitan más explicación:
-
El sistema de tipos en TypeScript está diseñado para ser opcional para que tu JavaScript sea TypeScript.
-
TypeScript no bloquea la emisión de JavaScript en presencia de errores de tipo, permitiéndote actualizar progresivamente tu JS a TS.
Ahora vamos a empezar con la sintaxis del sistema de tipos de TypeScript. Así podrás empezar a usar estas anotaciones en tu código inmediatamente y ver el beneficio. Esto te preparará para una inmersión más profunda más adelante.
Como se mencionó antes los tipos se anotan usando la sintaxis :TypeAnnotation
. Cualquier cosa que esté disponible en el espacio de declaración de tipos puede utilizarse como una anotación de tipo.
El siguiente ejemplo demuestra anotaciones de tipo para variables, parámetros de función y valores de retorno de función:
var num: number = 123;function identity(num: number): number {return num;}
Tipos primitivos
Los tipos primitivos de JavaScript están bien representados en el sistema de tipos de TypeScript. Esto significa string
, number
, boolean
como se demuestra a continuación:
var num: number;var str: string;var bool: boolean;num = 123;num = 123.456;num = '123'; // Errorstr = '123';str = 123; // Errorbool = true;bool = false;bool = 'false'; // Error
Arrays
TypeScript proporciona una sintaxis de tipos dedicada a los arrays para facilitar la anotación y documentación de tu código. La sintaxis consiste básicamente en posponer a cualquier anotación de tipo válida (por ejemplo,
:boolean
). Le permite realizar de forma segura cualquier manipulación de arrays que haría normalmente y le protege de errores como asignar un miembro del tipo incorrecto. Esto se demuestra a continuación:
var boolArray: boolean;boolArray = ;console.log(boolArray); // trueconsole.log(boolArray.length); // 2boolArray = true;boolArray = ;boolArray = 'false'; // Error!boolArray = 'false'; // Error!boolArray = ; // Error!
Interfaces
Las interfaces son la forma principal en TypeScript de componer múltiples anotaciones de tipo en una sola anotación con nombre. Considera el siguiente ejemplo:
interface Name {first: string;second: string;}var name: Name;name = {first: 'John',second: 'Doe'};name = { // Error : `second` is missingfirst: 'John'};name = { // Error : `second` is the wrong typefirst: 'John',second: 1337};
Aquí hemos compuesto las anotaciones first: string
+ second: string
en una nueva anotación Name
que refuerza las comprobaciones de tipo en miembros individuales. Las interfaces tienen mucho poder en TypeScript y dedicaremos una sección entera a cómo puedes usar eso en tu beneficio.
Anotación de tipo inline
En lugar de crear un nuevo interface
puedes anotar cualquier cosa que quieras inline usando :{ /*Structure*/ }
. El ejemplo anterior presentado de nuevo con un tipo inline:
var name: {first: string;second: string;};name = {first: 'John',second: 'Doe'};name = { // Error : `second` is missingfirst: 'John'};name = { // Error : `second` is the wrong typefirst: 'John',second: 1337};
Los tipos inline son geniales para proporcionar rápidamente una anotación de tipo única para algo. Le ahorra la molestia de tener que pensar en un nombre de tipo (potencialmente malo). Sin embargo, si te encuentras poniendo la misma anotación de tipo en línea varias veces es una buena idea considerar la refactorización en una interfaz (o un type alias
cubierto más adelante en esta sección).
Tipos especiales
Además de los tipos primitivos que se han cubierto hay unos pocos tipos que tienen un significado especial en TypeScript. Estos son any
, null
, undefined
, void
.
cualquier
El tipo any
ocupa un lugar especial en el sistema de tipos de TypeScript. Te da una escotilla de escape del sistema de tipos para decirle al compilador que se largue. any
es compatible con todos y cada uno de los tipos del sistema de tipos. Esto significa que cualquier cosa puede ser asignada a él y puede ser asignada a cualquier cosa. Esto se demuestra en el siguiente ejemplo:
var power: any;// Takes any and all typespower = '123';power = 123;// Is compatible with all typesvar num: number;power = num;num = power;
Si estás portando código JavaScript a TypeScript, vas a ser muy amigo de any
al principio. Sin embargo, no te tomes esta amistad demasiado en serio, ya que significa que depende de ti asegurar la seguridad de tipos. Básicamente le estás diciendo al compilador que no haga ningún análisis estático significativo.
nulos e indefinidos
Cómo son tratados por el sistema de tipos depende de la bandera del compilador strictNullChecks
(cubrimos esta bandera más adelante). Cuando en strictNullCheck:false
, el null
y undefined
literales de JavaScript son efectivamente tratados por el sistema de tipos igual que algo de tipo any
. Estos literales pueden ser asignados a cualquier otro tipo. Esto se demuestra en el siguiente ejemplo:
var num: number;var str: string;// These literals can be assigned to anythingnum = null;str = undefined;
:void
Use :void
para significar que una función no tiene un tipo de retorno:
function log(message): void {console.log(message);}
Genéricos
Muchos algoritmos y estructuras de datos en informática no dependen del tipo real del objeto. Sin embargo, usted todavía quiere hacer cumplir una restricción entre varias variables. Un simple ejemplo de juguete es una función que toma una lista de elementos y devuelve una lista invertida de elementos. La restricción aquí es entre lo que se pasa a la función y lo que es devuelto por la función:
function reverse<T>(items: T): T {var toreturn = ;for (let i = items.length - 1; i >= 0; i--) {toreturn.push(items);}return toreturn;}var sample = ;var reversed = reverse(sample);console.log(reversed); // 3,2,1// Safety!reversed = '1'; // Error!reversed = ; // Error!reversed = 1; // Okayreversed = ; // Okay
Aquí estás diciendo básicamente que la función reverse
toma un array (items: T
) de algún tipo T
(fíjate en el parámetro de tipo en reverse<T>
) y devuelve un array de tipo T
(fíjate en : T
). Como la función reverse
devuelve elementos del mismo tipo que toma, TypeScript sabe que la variable reversed
es también de tipo number
y le dará seguridad de tipo. De forma similar, si pasas un array de string
a la función inversa, el resultado devuelto es también un array de string
y obtienes una seguridad de tipo similar, como se muestra a continuación:
var strArr = ;var reversedStrs = reverse(strArr);reversedStrs = ; // Error!
De hecho, los arrays de JavaScript ya tienen una función .reverse
y TypeScript utiliza efectivamente genéricos para definir su estructura:
interface Array<T> {reverse(): T;// ...}
Esto significa que se obtiene seguridad de tipo al llamar a .reverse
sobre cualquier array como se muestra a continuación:
var numArr = ;var reversedNums = numArr.reverse();reversedNums = ; // Error!
Discutiremos más sobre la interfaz Array<T>
más adelante cuando presentemos lib.d.ts
en la sección Declaraciones de Ambiente.
Tipo Unión
Muy comúnmente en JavaScript se quiere permitir que una propiedad sea uno de los múltiples tipos, por ejemplo, un string
o un number
. Aquí es donde el tipo de unión (denotado por |
en una anotación de tipo, por ejemplo, string|number
) es útil. Un caso de uso común es una función que puede tomar un solo objeto o una matriz del objeto por ejemplo:
function formatCommandline(command: string|string) {var line = '';if (typeof command === 'string') {line = command.trim();} else {line = command.join(' ').trim();}// Do stuff with line: string}
Tipo Intersección
extend
es un patrón muy común en JavaScript donde se toman dos objetos y se crea uno nuevo que tiene las características de estos dos objetos. Un tipo de intersección le permite utilizar este patrón de una manera segura como se demuestra a continuación:
function extend<T, U>(first: T, second: U): T & U {return { ...first, ...second };}const x = extend({ a: "hello" }, { b: 42 });// x now has both `a` and `b`const a = x.a;const b = x.b;
Tipo de tupla
JavaScript no tiene soporte de tupla de primera clase. La gente generalmente sólo utiliza un array como una tupla. Esto es exactamente lo que soporta el sistema de tipos de TypeScript. Las tuplas pueden ser anotadas usando :
etc. Una tupla puede tener cualquier número de miembros. Las tuplas se demuestran en el siguiente ejemplo:
var nameNumber: ;// OkaynameNumber = ;// Error!nameNumber = ;
Combinando esto con el soporte de desestructuración en TypeScript, las tuplas se sienten bastante de primera clase a pesar de ser arrays por debajo:
var nameNumber: ;nameNumber = ;var = nameNumber;
Alias de tipo
TypeScript proporciona una sintaxis conveniente para proporcionar nombres para anotaciones de tipo que te gustaría usar en más de un lugar. Los alias se crean utilizando la sintaxis type SomeName = someValidTypeAnnotation
. A continuación se muestra un ejemplo:
type StrOrNum = string|number;// Usage: just like any other notationvar sample: StrOrNum;sample = 123;sample = '123';// Just checkingsample = true; // Error!
A diferencia de un interface
puedes dar un alias de tipo a literalmente cualquier anotación de tipo (útil para cosas como tipos de unión e intersección). Aquí hay algunos ejemplos más para que te familiarices con la sintaxis:
type Text = string | { text: string };type Coordinates = ;type Callback = (data: string) => void;
TIP: Si necesitas tener jerarquías de anotaciones de tipo utiliza un
interface
. Se pueden utilizar conimplements
yextends
TIP: Utilice un alias de tipo para estructuras de objetos más simples (como
Coordinates
) sólo para darles un nombre semántico. También cuando quieras dar nombres semánticos a los tipos Unión o Intersección, un alias de tipo es el camino a seguir.
Resumen
Ahora que puedes empezar a anotar la mayor parte de tu código JavaScript podemos entrar en los detalles de toda la potencia disponible en el sistema de tipos de TypeScript.