TypeScript para programadores de JavaScript
TypeScript tiene una relación inusual con JavaScript. TypeScript ofrece todas las funciones de JavaScript y una capa adicional además de ellas: el sistema de tipos de TypeScript.
Por ejemplo, JavaScript proporciona primitivos de lenguaje como string
y number
, pero no verifica que los hayas asignado de manera consistente. TypeScript lo hace.
Esto significa que tu código JavaScript en funcionamiento existente también es código TypeScript. El principal beneficio de TypeScript es que puede resaltar comportamientos inesperados en tu código, lo que reduce la posibilidad de errores.
Este tutorial proporciona una breve descripción general de TypeScript, centrándose en tsu sistema de tipos.
Tipos por Inferencia
TypeScript conoce el lenguaje JavaScript y generará tipos por ti en muchos casos. Por ejemplo, al crear una variable y asignarla a un valor particular, TypeScript usará el valor como su tipo.
let helloWorld = "Hello World";
let helloWorld: string
Al comprender cómo funciona JavaScript, TypeScript puede crear un sistema de tipos que acepte código JavaScript pero tenga tipos. Esto ofrece un sistema de tipos sin necesidad de agregar caracteres adicionales para hacer que los tipos sean explícitos en tu código. Así es como TypeScript sabe que helloWorld
es un string
en el ejemplo anterior.
Es posible que hayas escrito JavaScript en Visual Studio Code y hayas tenido el autocompletado del editor. Visual Studio Code utiliza TypeScript internamente para facilitar el trabajo con JavaScript.
Definiendo tipos
Puedes usar una amplia variedad de patrones de diseño en JavaScript. Sin embargo, algunos patrones de diseño dificultan la inferencia automática de tipos (por ejemplo, patrones que utilizan programación dinámica). Para cubrir estos casos, TypeScript admite una extensión del lenguaje JavaScript, que ofrece lugares para indicarle a TypeScript cuáles deberían ser los tipos.
Por ejemplo, para crear un objeto con un tipo inferido que incluye name: string
y id: number
, puedes escribir:
const user = {
name: "Hayes",
id: 0,
};
Puedes describir explícitamente la forma de este objeto usando una declaración de interface
:
interface User {
name: string;
id: number;
}
Luego puedes declarar que un objeto JavaScript se ajusta a la forma de tu nueva interface
usando una sintaxis como : TypeName
después de una declaración de variable:
const user: User = {
name: "Hayes",
id: 0,
};
Si proporcionas un objeto que no coincide con la interfaz que proporcionaste, TypeScript te advertirá:
interface User {
name: string;
id: number;
}
const user: User = {
username: "Hayes",
id: 0,
};
Type '{ username: string; id: number; }' is not assignable to type 'User'.
Object literal may only specify known properties, and 'username' does not exist in type 'User'.
Dado que JavaScript admite clases y programación orientada a objetos, TypeScript también lo hace. Puedes utilizar una declaración de interfaz con clases:
interface User {
name: string;
id: number;
}
class UserAccount {
name: string;
id: number;
constructor(name: string, id: number) {
this.name = name;
this.id = id;
}
}
const user: User = new UserAccount("Murphy", 1);
Puedes usar interfaces para anotar parámetros y devolver valores a funciones:
function deleteUser(user: User) {
// ...
}
function getAdminUser(): User {
//...
}
Ya existe un pequeño conjunto de tipos primitivos disponibles en JavaScript: boolean
, bigint
, null
, number
, string
, symbol
y undefined
, que puedes usar en una interfaz. TypeScript amplía esta lista con algunos más, como any
(permitir cualquier cosa), unknown
↗ (asegúrate de que alguien que use este tipo declare cuál es el tipo), never
↗ (no es posible que este tipo pueda suceder), y void
(una función que devuelve undefined
o no tiene valor de retorno).
Verás que hay dos sintaxis para construir tipos: Interfaces y tipos ↗. Deberías preferir interface
. Utiliza type
cuando necesites funciones específicas.
Componiendo tipos
Con TypeScript, puedes crear tipos complejos combinando tipos simples. Hay dos formas populares de hacerlo: con unions y con generics.
Uniones
Con una unión, puedes declarar que un tipo podría ser de uno de muchos tipos. Por ejemplo, puedes describir un tipo boolean
como true
o false
:
type MyBool = true | false;
Nota: Si pasas el cursor sobre MyBool
en un editor compatible, verás que está clasificado como boolean
. Esa es una propiedad del Sistema de Tipo Estructural. Más sobre esto a continuación.
Un caso de uso popular para los tipos de unión es describir el conjunto de string
o number
literales que un valor se permite tener:
type WindowStates = "open" | "closed" | "minimized";
type LockStates = "locked" | "unlocked";
type PositiveOddNumbersUnderTen = 1 | 3 | 5 | 7 | 9;
Las uniones también brindan una manera de manejar diferentes tipos. Por ejemplo, puedes tener una función que tome un array
o un string
:
function getLength(obj: string | string[]) {
return obj.length;
}
Para conocer el tipo de una variable, puedes usar typeof
:
Tipo | Predicado |
---|---|
string | typeof s === "string" |
number | typeof n === "number" |
boolean | typeof b === "boolean" |
undefined | typeof undefined === "undefined" |
function | typeof f === "function" |
array | Array.isArray(a) |
Por ejemplo, puedes hacer que una función devuelva diferentes valores dependiendo de si se le pasa una cadena o un array:
function wrapInArray(obj: string | string[]) {
if (typeof obj === "string") {
return [obj];
(parameter) obj: string
}
return obj;
}
Generics
Los generics proporcionan variables a los tipos. Un ejemplo común es un array. Un array sin generics podría contener cualquier cosa. Una array con generics puede describir los valores que contiene el array.
type StringArray = Array<string>;
type NumberArray = Array<number>;
type ObjectWithNameArray = Array<{ name: string }>;
Puedes declarar tus propios tipos que usan generics:
interface Backpack<Type> {
add: (obj: Type) => void;
get: () => Type;
}
// Esta línea es un atajo para decirle a TypeScript que hay una constante
// llamada `backpack` y que no se preocupe por su origen.
declare const backpack: Backpack<string>;
// object es una cadena, porque lo declaramos anteriormente como la parte variable de Backpack.
const object = backpack.get();
// Dado que la variable de backpack es una cadena, no puedes pasar un número a la función agregar.
backpack.add(23);
Argument of type 'number' is not assignable to parameter of type 'string'.
Sistema de Tipo Estructural
Uno de los principios básicos de TypeScript es que la verificación de tipos se centra en la forma que tienen los valores. A esto a veces se le llama “tipificación pato” o “tipificación estructural”.
En un sistema de tipo estructural, si dos objetos tienen la misma forma, se consideran del mismo tipo.
interface Point {
x: number;
y: number;
}
function logPoint(p: Point) {
console.log(`${p.x}, ${p.y}`);
}
// logs "12, 26"
const point = { x: 12, y: 26 };
logPoint(point);
La variable point
nunca se declara como del tipo Point
. Sin embargo, TypeScript compara la forma de point
con la forma de Point
en la verificación de tipo. Tienen la misma forma, por eso pasa el código.
La coincidencia de formas solo requiere que un subconjunto de los campos del objeto coincida.
const point3 = { x: 12, y: 26, z: 89 };
logPoint(point3); // logs "12, 26"
const rect = { x: 33, y: 3, width: 30, height: 80 };
logPoint(rect); // logs "33, 3"
const color = { hex: "#187ABF" };
logPoint(color);
Argument of type '{ hex: string; }' is not assignable to parameter of type 'Point'.
Type '{ hex: string; }' is missing the following properties from type 'Point': x, y
No hay diferencia entre cómo las clases y los objetos se ajustan a las formas:
class VirtualPoint {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
const newVPoint = new VirtualPoint(13, 56);
logPoint(newVPoint); // logs "13, 56"
Si el objeto o clase tiene todas las propiedades requeridas, TypeScript dirá que coinciden, independientemente de los detalles de implementación.
Próximos pasos
Esta fue una breve descripción general de la sintaxis y las herramientas utilizadas en el TypeScript cotidiano. Desde aquí podrás:
- Lee el Manual completo de principio a fin
- Explora los ejemplos de Playground ↗