Guía de TypeScript en Deno

TypeScript es un lenguaje que genera opiniones polarizadas. Por un lado, aporta seguridad y claridad al código, pero por otro, puede hacerlo más extenso y menos intuitivo.

Aprenderemos los fundamentos de TypeScript para que puedas aprovecharlo al máximo en Deno de manera efectiva.

#Fundamentos y compatibilidad con JavaScript

Aunque ha habido varios intentos de hacer JavaScript más seguro con types, TypeScript ha sido el más exitoso, convirtiéndose en un estándar de la industria. Su fortaleza radica en ser un superconjunto de JavaScript estándar, lo que significa que cualquier código JavaScript válido también funciona en TypeScript. Esto permite utilizarlo sin necesidad de aprenderlo todo de inmediato.

Por ejemplo, en un archivo TypeScript, al crear una variable llamada num y asignarle un valor numérico:

main.ts
let num = 7;

Al pasar el cursor sobre la variable, automáticamente se identifica como número.

Esto resulta especialmente útil al trabajar después con la variable, sobre todo cuando podríamos olvidar su tipo de dato.

Esta funcionalidad ayuda a prevenir errores en tiempo de ejecución, como asignar accidentalmente un valor de tipo incorrecto.

Al cambiar la variable a una constante, TypeScript muestra su valor al pasar el cursor sobre ella, eliminando la necesidad de buscarlo en el código.

main.ts
-let num = 7;
+const num = 7;

Esta documentación instantánea e integrada, conocida como IntelliSense, es invaluable cuando revisamos código después de semanas sin tocarlo, y resulta esencial en proyectos empresariales con equipos grandes.

TypeScript puede inferir automáticamente el tipo de una variable según su valor asignado. Sin embargo, cuando una variable no tiene un valor inicial, podemos usar una anotación de type colocando dos puntos después del nombre, seguido del type esperado. Por ejemplo, si definimos una variable como string, TypeScript marcará error al intentar asignarle un número.

main.ts
let num = 7;
 
+let str: string;
 
str = 7;

#Comprobando types en Deno

Deno tiene una característica particular: aunque compile y ejecute código con types incorrectos, podemos forzar la verificación usando la opción --check:

deno run --check main.ts

Esto activará errores de TypeScript y evitará la ejecución del código.

Alternativamente, deno check permite verificar types sin ejecutar el código:

deno check main.ts

#Types en Funciones

La seguridad de los types es especialmente útil en funciones. Por ejemplo, con una función multiply, Deno puede marcar error si los parámetros tienen tipo implícito any:

main.ts
export function multiply(a: any, b: any) {
return a * b;
}

El uso de any no es recomendable, y Deno opera en modo estricto por defecto, prohibiendo tipos any implícitos.

La solución es definir explícitamente los types de parámetros.

Aunque es posible desactivar el modo estricto o la regla de tipo implícito any en la configuración de Deno, no es una práctica recomendada:

deno.json
{
//
"compilerOptions": {
"noImplicitAny": false
}
}

La mejor práctica es definir anotaciones de type claras para parámetros y valores de retorno:

main.ts
-export function multiply(a: any, b: any) {
+export function multiply(a: number, b: number): number {
return a * b;
}

#Interfaces y types personalizados

TypeScript permite definir types estrictos para objetos con múltiples tipos de datos. Por ejemplo, para un objeto human con propiedades variadas, podemos crear una interfaz reutilizable:

const human = {
dna: "AGTC",
age: 39,
sex: true,
};
 
let human2: Human;
 
interface Human {
dna: string;
age: number;
sex: boolean;
}

También podemos usar la palabra clave type para definir estructuras de objetos, aunque existen diferencias sutiles en casos complejos:

-interface Human {
+type Human {
dna: string;
age: number;
- sex: boolean;
+ sex: HumanSex;
}
 
+type HumanSex = 'male' | 'female' | 'cyborg'

Los types de unión permiten que una variable acepte múltiples valores específicos, marcando error para valores no válidos.

#Genéricos en TypeScript

TypeScript incluye características avanzadas como genéricos, que permiten crear componentes o types reutilizables. Por ejemplo:

type Dog = { name: string };
type Cat = { name: string };
 
const animal = { name: "clemen" };
 
type Robot<T> = {
chip: string;
animal: T;
};
 
const robotCat: Robot<Cat> = { animal, chip: "Intel" };
const robotDog: Robot<Dog> = { animal, chip: "AMD" };

Los genéricos funcionan como parámetros para types, siendo especialmente útiles en estructuras como promesas:

async function doAsync(): Promise<Cat> {
return { name: 'clemen'};
}

#Integración con librerías y el DOM

Al trabajar con librerías existentes, es preferible usar sus types incorporados. Para el frontend, podemos incluir types del DOM usando referencias o configuración global:

/// <reference lib="dom" />
 
let doc: Document
let el: HTMLElement
deno.json
{
//...
"compilerOptions": {
"lib": ["dom", "deno.ns"]
}
}

Deno permite ejecutar archivos JavaScript directamente y verificar types en ellos usando la directiva ts-check:

main.js
// @ts-check
 
let str = "mi string"
 
str = 23