TypeScript
Fundamentos
- ¿Que es TypeScript?
- Si conoces JavaScript conoces TypeScript
- ¿Por qué usar TypeScript?
- Se puede usar tanto como en el frontend como en el backend
- Instalación del compilador de TypeScript
- Usando el compilador de TypeScript
- El archivo de configuración de TypeScript
- Generación del archivo tsconfig.json
- Usando el archivo tsconfig.json
- Tipado en TypeScript
- Tipos básicos
- Any, Void y Never
- Null y Undefined
- Object
- Array
- Tuple
- Enum
- Unión de Tipos, Alias y Tipos Literales
- Aserciones de tipo
- Funciones en TypeScript
- Tipo de retorno en TypeScript
- Interfaces
- Interfaces: Propiedades opcionales
- Interfaces: Propiedades de solo lectura
- Extensión de interfaces
- Clases
Ejemplos practicos
Utilidades
Fundamentos
¿Que es TypeScript?
- Lenguaje de programación tipado
- Lenguaje de alto nivel
- Genera como resultado código JavaScript
- Código abierto
- Desarrollo en cualquier sistema operativo
- El código puede ejecutarse en cualquier navegador o plataforma
Si conoces JavaScript conoces TypeScript
¿Por qué usar TypeScript?
- Programación orientada a objetos
- Potencia el código JavaScript
- Mayor productividad
- Poderoso sistema de tipos
- Compila a ES5, ES6 y más
- Proyecto muy activo/Open Source
- Actualizaciones periódicas
- Comunidad creciente
Se puede usar tanto como en el frontend como en el backend
Instalación del compilador de TypeScript
npm install -g typescript
Para verificar que está correctamente instala se usa el comando
tsc --version
Usando el compilador de TypeScript
De manera práctica se puede ejecutar
tsc hello.ts
ls
hello.js hello.ts
Ahora se necesita un runtime de JavaScript que es quien va a ejecutar el código JavaScript resultante
node hello.js
El compilador de TypeScript tiene una opción que permite registrar los cambios.
tsc --watch hello.ts
El archivo de configuración de TypeScript
¿Qué es el archivo de configuración tsconfig.js?
- Especifica la raíz de un proyecto TypeScript
- Permite configurar opciones para el compilador
Generación del archivo tsconfig.json
Se ejecuta el siguiente comando:
tsc --init
El archivo consta de un objeto json
{
"extends": "./configs/base", //-> Se usa para heredar las configuraciones de un archivo json general
"compileOnSave": true, //->Seusa para que cada vez que se guarde un archivo se compile el código automaticamente
"compilerOptions": {
"target": "es2016", //-> Especifica la versión de ES
"module": "commonjs", //-> Especifica la configuración para generación de modulos
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true, //-> Hablita la verificación estricta de tipos
"skipLibCheck": true,
"removeComments": true //-> Permite eliminar los comentarios al codigo fuente
},
"include": ["src/**/*.ts"], //-> Indica que archivos van a ser procesados por ek compilador
"exclude": ["node_modules", "**/*.test.ts"] //->Excluye archicos o directorios del preceso de compilación
}
Usando el archivo tsconfig.json
tsc ------------------> Busca la configuración
tsc --project gamcode --------> Especifica un directorio que contiene la configuración
tsc files.ts ------------------> Omite la configuración
Tipado en TypeScript
- Explícito
- Define una sintaxis para la creación de variables con tipo de dato.
- Inferido
- TypeScript tiene la habilidad de "deducir" el tipo en función de un valor
Tipado en TypeScript Explícito
Tipado en TypeScript Implícito
Tipos básicos
Tipos de datos simples en TypeScript
Tipos primitivos
Number
//declaración explícita
let phone: number;
phone = 5;
phone = "hola"; //-> Error
//declaración inferida
let phoneNumber = 2; //-> Al hacer hover sobre la variable se puede ver que typeScript le asigna un tipo
phoneNumber = 232;
phoneNumber = true; //-> Error
Boolean
let isPro: boolean;
isPro = false;
String
let username: string = "gamcode";
//Con template string
let userInfo: string = `${username} este es mi username`;
Any, Void y Never
Tipo: Any
- Usado para capturar valores dinámicos
- Los valores pueden cambiar de tipo en el tiempo:
- APIs externas
- Librerías de terceros
Se lo usa como último recurso, no es buena práctica.
let idUser: any = 1;
idUser = "1"; // -> No da error
console.log("idUser", idUser);
Tipo: Void
- void es lo opuesto de any: representa la ausencia de tipo
- Comúnmente se usa como tipo de retorno en funciones (en la declaración de las mismas)
- Tambien se las utiliza cuando las funciones no retornan ningún valor
//Any
function showInfo(user: any): any {
console.log("User info", user.id, user.username, user.firstName);
//return "hola"; -> Si tuviera el return estaría devolviendo ese estring
}
showInfo({ id: 1, username: "gamcode", firstName: "gabriel" });
Tipo void, como tipo de dato en variable
let unusable: void;
unusable = null;
unusable = undefined;
//Hay que deshabilitar el modo strict de tsconfig.json
Tipo: Never
- Representa el tipo de valor que nunca ocurre
- Funciones que lanzan exepciones
- Funciones que nunca retornan un valor
- Funciones con ciclos infinitos, que no permiten que se finalice
//Never
function handleError(code: number, message: string): never {
//Proceso de codigo aqui
//Se genera un mensaje
throw new Error(`${message}. Code: ${code}`);
}
try {
handleError(404, "Not Found");
} catch (error) {}
Null y Undefined
- En TypeScript, ambos "valores" tienen sus respectivos:
- null
- undefined
let nullVariable: null;
nullVariable = null;
nullVariable = 1; // Error
let undefinedVariable: undefined;
undefinedVariable = undefined;
undefinedVariable = 1; // Error
Null y Undefined como subtipos
- Null y Undefined se pueden asumir como subtipos de los otros tipos de datos, por ejemplo any
- Significa que se puede asignar null y undefined a una variable de tipo string, por ejemplo
let albumName: string;
albumName = null;
albumName = undefined;
//Error hay que descomentar la linea en la aparece la opción "strictNullChecks en el archivo tsconfig.json para que lo permita
Null y Undefined: La opción --strictNullChecks
- Solo permite asignar null y undefined a una variable de tipo any o sus tipos respectivos.
- Ayuda a evitar errores comunes en programción de apps en el ámbito JavaScript.
Otra opción es usar la bandera --strictNullChecks en la terminal:
tsc --watch src/main.ts --strictNullChecks
Object
Tipo: object
- object es el tipo de dato que representa un valor no primitivo.
- Es el tipo de variable que no sea number, string, boolean, null, undefined, etc.
let user: object = {};
user = {
id: 1,
username: gamcode,
};
console.log("user.username", user.username); //Error
Object vs object
- Object: instancia de la clase Object de JavaScript
- object: tipo de valores no primitivos. Con este tipo no se puede acceder a las propiedades del objeto.
//Object vs object (Clase JS vs tipo TS)
const myObj = {
id: 1,
username: gamcode,
};
const isInstance = myObj instanceof Object; // retorna true o false
console.log("isInstance", isInstance);
console.log("myObj.username", myObj.username); //Ok
Array
- Al igual que JavaScript, TypeScript permite definir un arreglo para contener un conjunto de valores.
- Usa dos notaciones: [] y Array<tipo>
//Corchetes []
let users: string[];
users = ["Gabriel", "Alejandro"];
users = [true, 1, "Alejandro"]; // Error
//Array<TIPO>
//<> -> Se conocen como genéricos
let pictureTtiles: Array<string>;
pictureTtiles = ["michi", "pachicha"];
//Accediendo a los valores en un Array
console.log("first user", users[0]);
console.log("first title", pictureTtiles[0]);
//Propiedades en Array
console.log("users.length", users.length);
//Uso de funciones en Arrays
console.log(users.push("lolo"));
console.log(users.sort());
console.log("users", users);
Tuple
- Las tuplas permiten expresar un arreglo con un número fijo de elementos.
- Los tipos de datos son conocidos.
// [1,"user"]
let user: [number, string];
user = [1, "gamcode"];
console.log("user", user);
console.log("username", user[1]);
console.log("username.length", user[1].length);
console.log("id", user[0]);
//Tuplas con varios valores
//id, username, isPro
let userInfo: [number, string, boolean];
userInfo = [2, "gamcode", true];
console.log("userInfo", userInfo);
//Arreglo de tuplas
let array: [number, string][] = [];
array.push([1, "michi"]);
array.push([2, "lolo"]);
array.push([3, "lensQueen"]);
array.push([4, "pepe"]);
array.push([5, "lore"]);
console.log("array", array);
//Uso de funciones Array
//lensQueen -> lensQueen001
array[2][1] = array[2][1].concat("001");
console.log("array", array);
console.log(array[2][1]);
Enum
- Los enumerados permiten definir un conjunto de constantes con nombre.
- Tienen la ventaja de adaptarse al contexto de la aplicación.
// Orientacion para fotografias
//En JS
// const landscape = 0;
// const portrait = 1;
// const square = 2;
// const panorama = 3;
//En TS
enum PhotoOrientation {
Landscape = 0, //De forma explicita
Portrait, //1 De forma implicita
Square, //2
Panorama, //3
}
const landscape: PhotoOrientation = PhotoOrientation.Landscape;
console.log("landscape", landscape); // -> muestra el num 0
console.log("Landscape", PhotoOrientation[landscape]);
//Personalización de enums
enum PictureOrientation {
Landscape = 10,
Portrait, //11
Square, //12
Panorama, //13
}
console.log("portrait", PictureOrientation.Portrait); // muestra el num 11
//Se considera las claves para personalizar
enum Country {
Argentina = "arg",
Colombia = "col",
Mexico = "mex",
EEUU = "usa",
Espana = "esp",
}
const country: Country = Country.Colombia;
console.log("country", country);
Unión de Tipos, Alias y Tipos Literales
Unión de tipos
- En TypeScript se puede definir una variable con múltiples tipos de datos: Union Type.
- Se usa el símbolo de pipe ("|") entre los tipos.
// 10, "10", -> Debe soportar numeros y cadenas
let idUser: number | string;
idUser = 10;
idUser = "10";
//Buscar username dado un ID
function getUserNameById(id: number | string){
// Logica de negocio, find the user
return "gamcode";
}
getUserNameById(20);
getUserNameById("20");
Alias de tipos
- TypeScript permite crear un alias como nuevo nombre para un tipo.
- El alias se puede aplicar también a un conjunto o combinación de tipos.
- Se usa la palabra reservada type
// 10, "10", -> Debe soportar numeros y cadenas
type IdUser = number | string;
type Username = string;
let idUser: IdUser;
idUser = 10;
idUser = "10";
//Buscar username dado un ID
function getUserNameById(id: IdUser): Username{
// Logica de negocio, find the user
return "gamcode";
}
getUserNameById(20);
getUserNameById("20");
Tipos literales
- Una variable con un tipo literal puede contener únicamente una cadena del conjunto.
- Se usan cadenas como "tipos", combinados con el símbolo de pipe ("|") entre ellos.
// Tipos literales
//Solo fotografias de 100x100, 500x500, 1000x1000
type SquareSize = "100x100" | "500x500" | "1000x1000";
let smallPicture: SquareSize = "200x200"; //Error xq no pertenece al tipo
let smallPicture: SquareSize = "100x100";
let mediumPicture: SquareSize = "500x500";
Aserciones de tipo
- Cuando el programador puede conocer más que TypeScript sobre el valor de una variable.
- Es un mensaje al compilador: "Confía en mí, sé lo que hago"
- Se parece al casting de tipos en otros lenguajes de programción. Sin embargo no es lo mismo.
- Usa dos sintaxis: <Angle Bracket> y (variable as tipo)
// <tipo> // Angle Bracket Sintax
let username: any;
username = "gamcode";
//tenemos una cadena, TS confia en mi!
let message: string =
(<string>username).length > 5
? `Welcome ${username}`
: "Username is too short";
console.log("message", message);
let usernameWithId: any = "gamcode 1";
// Como obtener el username?
username = (<string>usernameWithId).substring(0, 10);
console.log("Username only: ", username); //-> Username only: gamcode
//Sintaxis "as"
message =
(username as string).length > 5
? `Welcome ${username}`
: "Username is too short";
let helloUser: any;
helloUser = "hello michi";
username = (helloUser as string).substring(6);
console.log("username", username); // michi
Funciones en TypeScript
- Los parámetros en las funciones son tipados.
- Se pueden definir parámetros opcionales.
- El tipo de retorno puede ser un tipo básico, enum, tipo literal o una combinación de ellos
// Crear una fotografia
type SquareSize = "100x100" | "500x500" | "1000x1000";
function createPicture(title: string, date: string, size: SquareSize) {
//Se crea la fotografia
console.log("create picture using", title, date, size);
}
createPicture("michi", "02-04-2022", "100x100");
createPicture("meme", "02-03-2022"); //Error, espera el ultimo parámetro
//Se soluciona con parametros opcionales -> "?"
function createPicture(title?: string, date?: string, size?: SquareSize) {
//Se crea la fotografia
console.log("create picture using", title, date, size);
}
createPicture("meme", "02-03-2022"); //Ok
Tipo de retorno en TypeScript
// Dos tipos de retorno
function handleError(code: number, message: string): never | string {
//Procesamiento del codigo, mensaje
if (message === "error") {
throw new Error(`${message}. Code error: ${code}`);
} else {
return "An error has ocurred";
}
}
//Verficacion de la funcion
let result = handleError(200, "ok"); //retorna un string
console.log("result", result);
result = handleError(404, "error"); // retorna un never, que hace referencia a lanzar un error
console.log("result", result);
function handleError(code: number, message: string): never | string {
//Procesamiento del codigo, mensaje
if (message === "error") {
throw new Error(`${message}. Code error: ${code}`);
} else {
return "An error has ocurred";
}
}
//Para capturar el error independientemente si hay un string o error
try {
let result = handleError(200, "ok");
console.log("result", result);
result = handleError(404, "error");
console.log("result", result);
} catch (error) {}
Interfaces
Entendiendo las interfaces
Las interfaces en TypeScript constituyen una forma poderosa de definir "contratos" tanto para tu proyecto, como para el código externo del mismo.
enum PictureOrientation {
Landscape,
Portrait,
Square,
Panorama,
}
//Funcion para mostrar una fotografia
function showPicture(picture:
{title: string,
date: string,
orientation : PhotoOrientation}){
console.log(`[title: ${picture.title},
date: ${picture.date},
orientation: ${picture.orientation} ]`)
}
let myPic = {
title: "Test title",
date: "2020-03",
orientation: PhotoOrientation.Landscape
};
showPicture(myPic)
showPicture({
title: "Test title",
date: "2020-03",
orientation: PhotoOrientation.Portrait,
// extra: "test", //Error
})
Aplicando interfaces
enum PictureOrientation {
Landscape,
Portrait,
Square,
Panorama,
}
interface Picture {
title: string;
date: string;
orientation: PhotoOrientation;
}
function showPicture(picture: Picture) {
console.log(`[title: ${picture.title},
date: ${picture.date},
orientation: ${picture.orientation} ]`);
}
Interfaces: Propiedades opcionales
No todas las propiedades de una interfaz podrían ser requeridas.
Establecemos una propiedad como opcional con el símbolo (?) después del nombre.
enum PictureOrientation {
Landscape,
Portrait,
Square,
Panorama,
}
interface PictureConfig {
title?: string;
date?: string;
orientation?: PhotoOrientation;
}
function generatePicture(config: PictureConfig) {
const pic = { title: "Default", date: "2020-03" };
if (config.title) {
pic.title = config.title;
}
if (config.date) {
pic.date = config.date;
}
return pic;
}
let picture = generatePicture({});
console.log("picture", picture);
picture = generatePicture({ title: "Travel Pic" });
console.log("picture", picture);
Interfaces: Propiedades de solo lectura
Algunas propiedades de la interfaz podrían no ser modificables un vez creado el objeto.
Esto es posible usando readonly antes del nombre de la propiedad.
//Interfaz
interface User {
readonly id: number; //Solo lectura
user: string;
isPro: boolean;
}
let user: User;
user = { id: 10, username: "gamcode", isPro: true };
console.log("user", user);
user.id = 20; //Error
user.username = "paparazzi";
console.log("user", user);
Extensión de interfaces
Las interfaces pueden extenderse unas de otras. Esto permite copiar los miembros ya definidos en una interfaz a otra, ganando flexibilidad y reusabilidad de componentes.
enum PhotoOrientation {
Landscape,
Portrait,
Square,
Panorama,
}
interface Entity {
id: number;
title: string;
}
interface Album extends Entity{
//Copia de los atributos de entity
description: string;
}
interface Picture extends Entity{
//Copia de los atributos de entity
orientation: PhotoOrientation
}
const album :Album = {
id: 1,
title: "Meetups",
description: "Community events around the world"
}
const picture: Picture = {
id: 1,
title: "Family",
orientation: PhotoOrientation.Landscape
}
let newPicture = {} as Picture;
newPicture.id = 2;
newPicture.title ="Moon";
console.log("album",album)
console.log("picture",picture)
console.log("newPicture",newPicture)
Clases
Definiendo Clases y Constructores
A partir de ECMAScript 2015 es posible construir clases y hacer uso del paradigma de la Programación Orientada a Objetos en JavaScript.
TypeScript permite aplicar estas técnicas sin tener que esperar por otra versión.
enum PhotoOrientation {
Landscape,
Portrait,
Square,
Panorama,
}
class Picture {
//Propiedades
id: number;
title: string;
orientation: PhotoOrientation;
constructor(id: number,
title: string,
orientation: PhotoOrientation){
this.id = id;
this.title = title;
this.orientation = orientation;
}
//Comportamiento
toString(){
return `[id : ${this.id},
title: ${this.title},
orientation: ${this.orientation}]`
}
}
class Album {
id: number;
title: string;
pictures: Picture[];
constructor(id: number, title: string, ){
this.id = id;
this.title = title;
this.pictures = [];
}
addPicture(picture: Picture){
this.pictures.push(picture)
}
}
const album: Album = new Album(1, "Personal Pictures");
const picture: Picture = new Picture(1, "gamcode session", PhotoOrientation.Square);
album.addPicture(picture);
console.log("album",album)
Miembros públicos
TypeScript define un modificador de acceso público por defecto para los miembros de clase.
También es posible marcar un miembro como público usando la palabra reservada public
class Picture {
//Propiedades
public id: number;
public title: string;
public orientation: PhotoOrientation;
public constructor(id: number, title: string, orientation: PhotoOrientation) {
this.id = id;
this.title = title;
this.orientation = orientation;
}
//Comportamiento
public toString() {
return `[id : ${this.id},
title: ${this.title},
orientation: ${this.orientation}]`;
}
}
//Accediendo a los miembros publicos
picture.id = 100; //public
picture.title = "Another title"; // public
album.title = "Personal Activities";
console.log("album", album);
Miembros privados
TypeScript define una manera propia de declarar o marcar un miembro como privado usando la palabra reservada private
Ejemplos practicos
Primer ejemplo
console.log("Hello world");
Utilidades
Crear función contructora para arrays (?
console.log("Construyendo HAHAHAH");