Typescript

Introducción

Typescript es un superset de Javascript, lo cual no significa que agrega nuevas funcionalidades al lenguaje de programación, más bien ayuda a escribir de una mejor manera el código Javascript mediante type safety y así evitando problemas en el momento de ejecución. Como lo hace es analizando el código estático previo a ser ejecutado.

Aquí hay algunos propósitos y beneficios clave de usar TypeScript:

  1. Tipado Estático:

    • Detección temprana de errores: TypeScript permite detectar errores en tiempo de desarrollo en lugar de en tiempo de ejecución, lo que ayuda a evitar errores comunes y a mejorar la estabilidad del código.

    • Autocompletado y documentación: Los editores de código y los entornos de desarrollo integrados (IDE) pueden proporcionar autocompletado, documentación en línea y comprobaciones de tipos gracias a la información de tipos disponible.

  2. Mejor Mantenimiento del Código:

    • Refactorización segura: Las herramientas de refactorización pueden ser más efectivas y seguras con TypeScript porque tienen más información sobre los tipos y las relaciones entre las diferentes partes del código.

    • Escalabilidad: TypeScript es especialmente útil en proyectos grandes y complejos, donde el código tiende a ser más difícil de mantener.

  3. Características Avanzadas del Lenguaje:

    • Interfaces y tipos personalizados: TypeScript permite definir interfaces y tipos personalizados, lo que ayuda a crear estructuras de datos claras y concisas.

    • Decoradores y metaprogramación: TypeScript soporta decoradores y otras características avanzadas que no están disponibles en JavaScript estándar.

  4. Compatibilidad con el Ecosistema JavaScript:

    • Transpilación a JavaScript: TypeScript se transpila a JavaScript, lo que significa que puedes usarlo en cualquier entorno donde se pueda ejecutar JavaScript, incluyendo navegadores web y Node.js.

    • Interoperabilidad: Puedes usar bibliotecas y frameworks de JavaScript existentes con TypeScript, y puedes gradualmente migrar un proyecto de JavaScript a TypeScript.

  5. Mejor Colaboración:

    • Consistencia en equipos grandes: TypeScript ayuda a equipos grandes a mantener un código más consistente y predecible.

    • Documentación implícita: Los tipos actúan como una forma de documentación implícita, haciendo que el código sea más fácil de entender para otros desarrolladores.

Nota: Las validaciones de Typescript al ser estáticas no se pueden hacer durante el runtime de la aplicación.

Como funciona

Tipos Primitivos

  1. string: Representa una cadena de texto.

  2. number: Representa números, ya sean enteros o de punto flotante.

  3. boolean: Representa valores de verdad (true o false).

  4. null y undefined: Representan la ausencia de valor.

Type

Se usa para definir tipos de variables y generalmente se usa para tipos primitivos.

Tipos Compuestos

  1. Arreglos (Array): Representa una lista de elementos del mismo tipo.

    1. ReadonlyArray: Este tipo de dato es un array, pero la principal diferencia con los arrays normales es que no existe ninguno de los métodos de mutación, tales como pop, push, shift, etc.

      const arr: ReadonlyArray = [1,2,3,4,5];

  2. Tuplas (Tuple): Representa un arreglo con un número fijo de elementos de tipos específicos.

  3. Objetos (Object): Representa una colección de propiedades con valores de tipos específicos.

Nota: Tener curidado con esta diferencia

1. number[]

  • Es un arreglo que puede contener cualquier cantidad de elementos, siempre que sean del tipo number.

  • No hay restricciones en la longitud del arreglo.


2. [number]

  • Es una tupla que describe un arreglo con exactamente un elemento, y ese elemento debe ser un number.

  • Si el arreglo tiene más o menos elementos, TypeScript mostrará un error.


Tipos Especiales

  1. any: Desactiva la verificación de tipos, permitiendo cualquier valor. Úsalo con precaución.

  2. unknown: Similar a any, pero más seguro porque aún requiere una verificación de tipo antes de usarlo.

    1. Unknown type, este tipo de dato es la mejora de any, ya que nos da la flexibilidad que en ocasiones queremos pero sin apagar por completo el análisis de código estático. unknown nos fuerza a hacer una verificación de typeof.

      Comparar con aserción de tipo:

      typeof input === "string | object | number";

      Un array:

      Array.isArray(input);

      Otra cosa que podemos hacer para asignar un tipo cuando no lo conocemos es, por ejemplo:

  3. void: Representa la ausencia de un valor, comúnmente utilizado en funciones que no retornan nada.

  4. never: El tipo de dato never, más que todo sirve para tipar a una función que nunca va a finalizar o sencillamente que pueda terminar el programa en el caso de lanar una excepción.

Cast

Pasar de un tipo de dato a otro.

Podemos definir como queremos que sea tratado un tipo de dato, le decimos a TypeScript que confíe en nuestro tipado. Que trate a esa variable como ese tipo de dato que le especificamos.

Angle bracket

Es lo mismo que as pero con otra sintaxis.

Tipos Literales

Los tipos literales permiten especificar valores exactos que una variable puede tener.

Tipos Unión y Tipos Intersección

  1. Unión (Union): Permite que una variable tenga más de un tipo.

  2. Intersección (Intersection): Combina múltiples tipos en uno solo.

Tipos de Funciones

Puedes definir los tipos de entrada y salida de una función.

Enum

Set de conjunto de opciones

Sobrecarga de funciones

Interfaces

Las interfaces funcionan muy similar a como lo hace type, pero en las interfaces solo aplica para los objetos y pueden ser heredadas.

DTOs Es una abreviatura para referirnos a Data Transfer Objects son separados en archivos aparte.

Omit y Pick type Estos tipos de datos nos permiten crear nuevas interfaces basadas de otras, pero omitiendo o seleccionando solo ciertos valores.

Las utilidades Omit y Pick son herramientas de TypeScript que permiten trabajar con interfaces o tipos, seleccionando o excluyendo propiedades de manera dinámica. Veamos qué hacen en el contexto que mencionaste:


1. Omit

  • Se utiliza para excluir una o más propiedades de un tipo o interfaz.

  • En tu ejemplo:

Aquí:

  1. Omit<EmployeeModel, 'id' | 'name'>:

    • Excluye las propiedades id y name de EmployeeModel.

    • El resultado es un tipo con las propiedades restantes: lastName, salary, y company.

  2. extends: La interfaz EmployeeModelDto hereda de este nuevo tipo.

  3. companyId: number;: Se agrega esta nueva propiedad a EmployeeModelDto.

El resultado final de EmployeeModelDto será:


2. Pick

  • Se utiliza para seleccionar una o más propiedades de un tipo o interfaz.

  • En tu ejemplo:

Aquí:

  1. Pick<EmployeeModel, 'id' | 'name'>:

    • Selecciona únicamente las propiedades id y name de EmployeeModel.

    • El resultado es un tipo con solo esas dos propiedades.

  2. extends: La interfaz EmployeeModelDto hereda de este nuevo tipo.

  3. companyId: number;: Se agrega esta nueva propiedad a EmployeeModelDto.

El resultado final de EmployeeModelDto será:


Diferencias Clave

Utilidad
Qué hace
Resultado

Omit

Excluye propiedades del tipo original

EmployeeModel menos las propiedades especificadas (id, name)

Pick

Selecciona únicamente las propiedades especificadas

Solo las propiedades id y name del tipo EmployeeModel


Cuándo usar Omit y Pick

  • Omit: Cuando quieres trabajar con la mayoría de las propiedades de un tipo, pero necesitas excluir algunas específicas.

  • Pick: Cuando quieres trabajar solo con unas pocas propiedades de un tipo, ignorando el resto.

Partial & Required Estos dos tipos de datos nos sirven para declarar que todos los campos de una interfaz son opcionales u obligatorios.

Readonly Con esta propiedad le decimos TypeScript que todos los parámetros solamente sean de lectura. Ejemplos:

Tipos de Utilidad

TypeScript ofrece varios tipos de utilidad para transformar y combinar tipos.

  • Partial<T>: Hace que todas las propiedades de T sean opcionales.

  • Required<T>: Hace que todas las propiedades de T sean requeridas.

  • Readonly<T>: Hace que todas las propiedades de T sean de solo lectura.

  • Pick<T, K>: Selecciona un subconjunto de las propiedades de T.

  • Omit<T, K>: Excluye un subconjunto de las propiedades de T.

Tipado por indice

Diferencias Clave Enum, Type e Interface

  1. Propósito y Uso:

    • enum: Utilizado para definir un conjunto finito de valores constantes.

    • type: Utilizado para crear alias de tipos, tipos compuestos y complejos.

    • interface: Utilizado para definir la estructura de objetos y puede ser extendida e implementada.

  2. Herencia y Extensión:

    • enum: No admite herencia.

    • type: No admite herencia, pero puede combinarse usando Union y Intersection.

    • interface: Admite la herencia múltiple y la implementación en clases.

  3. Flexibilidad:

    • enum: Menos flexible, se utiliza principalmente para valores constantes.

    • type: Muy flexible y puede ser utilizado para definir cualquier tipo de dato.

    • interface: Más estructurado, ideal para definir la forma de objetos.

  4. Compatibilidad:

    • En general, interface y type son intercambiables en muchos casos, pero interface es preferido para definir la estructura de objetos debido a su capacidad de extensión y mejor integración con la programación orientada a objetos.

Class ES6 y typescript

Se pueden declarar las variables de una clase de esta forma en el contructor (es necesario especificar si es pública o privada)

  • Protected: es un modificador de acceso que permite que los atributos y métodos de la clase sean accesibles desde la misma clase y desde las clases que heredan de esta.

  • Private: acceso de la misma clase.

  • super: La palabra clave super es usada para acceder y llamar funciones del padre de un objeto.

Abstract e implements

En TypeScript, abstract y implements son dos palabras clave que se utilizan para trabajar con clases y interfaces de una manera que facilita la programación orientada a objetos. Estas herramientas permiten definir y garantizar contratos de implementación y comportamientos comunes en jerarquías de clases.

Clases Abstractas (abstract)

Una clase abstracta es una clase que no se puede instanciar directamente. Se utiliza como una base para otras clases. Las clases abstractas pueden contener métodos abstractos (métodos sin implementación) y métodos concretos (métodos con implementación).

Las clases abstractas son tan genericas que no tiene sentido que sean instanciadas. Abstract se puede usar como interface también, ya que con interface no se puede tener atributos o métodos private o protected. Estos solo pueden se public.

Características de las Clases Abstractas

  1. No Instanciables: No se pueden crear instancias directamente de una clase abstracta.

  2. Métodos Abstractos: Pueden contener métodos sin implementación que deben ser implementados por las clases derivadas.

  3. Métodos Concretos: Pueden contener métodos con implementación que pueden ser heredados o sobrescritos por las clases derivadas.

  4. Propiedades Abstractas: Pueden definir propiedades abstractas que deben ser implementadas por las clases derivadas.

Ejemplo de Clase Abstracta

Interfaces (interface) y implements

Una interfaz en TypeScript define un contrato que las clases deben seguir. Las interfaces pueden declarar métodos y propiedades sin proporcionar implementaciones. Las clases que implementan una interfaz deben proporcionar implementaciones para todos los métodos y propiedades declaradas en la interfaz.

Características de las Interfaces

  1. Contrato de Implementación: Define un conjunto de métodos y propiedades que una clase debe implementar.

  2. Sin Implementación: No pueden contener implementaciones de métodos.

  3. Herencia Múltiple: Una clase puede implementar múltiples interfaces.

  4. Extensión: Las interfaces pueden extender otras interfaces.

Ejemplo de Interface y implements

Uso Combinado de Clases Abstractas e Interfaces

A menudo, se utilizan clases abstractas e interfaces en combinación para definir estructuras de código más robustas y flexibles.

Ejemplo Combinado

Resumen

  • Clases Abstractas (abstract):

    • No se pueden instanciar directamente.

    • Pueden contener métodos y propiedades abstractas (sin implementación).

    • Pueden contener métodos y propiedades concretas (con implementación).

  • Interfaces (interface):

    • Definen contratos de implementación.

    • No pueden contener implementaciones de métodos.

    • Pueden ser implementadas por las clases usando la palabra clave implements.

    • Pueden ser extendidas por otras interfaces.

  • implements:

    • Utilizado por las clases para declarar que implementan una o más interfaces.

    • Obliga a la clase a proporcionar implementaciones para todos los métodos y propiedades de las interfaces.

El uso de clases abstractas e interfaces en TypeScript permite crear jerarquías de clases flexibles y bien estructuradas, facilitando la reutilización de código y la adherencia a principios de diseño orientado a objetos como el principio de sustitución de Liskov y la segregación de interfaces.

Patron Singleton

Con typescript se puede crear un constructor privado usando el patron Singleton, nos previene crear múltiples instancias de una clase.

Genéricos

Un genérico es como un parámetro para tipos. Puedes pensar en los genéricos como una forma de decir "este código funcionará con cualquier tipo, solo dime qué tipo será cuando lo uses". En lugar de definir una función que solo funcione con un tipo específico, usas un genérico para que la función funcione con diferentes tipos de datos sin perder la seguridad de tipos.

Ejemplo Simple de una Función Genérica

Supongamos que queremos crear una función que tome un argumento y simplemente lo devuelva:

En este ejemplo:

  • T es un parámetro de tipo genérico. Podría ser cualquier nombre de variable (a menudo se usan T, U, V, etc.).

  • value: T significa que la función tomará un argumento value de cualquier tipo T.

  • : T después de la firma de la función indica que el tipo de retorno de la función será del mismo tipo T.

Puedes usar esta función con diferentes tipos de datos:

Inferencia de Tipos

TypeScript es capaz de inferir el tipo genérico automáticamente en muchos casos, por lo que no siempre es necesario especificar el tipo explícitamente:

Genéricos en Clases

Puedes también usar genéricos en clases para crear estructuras de datos más flexibles:

En este ejemplo, Box<T> es una clase genérica que puede contener cualquier tipo T. Puedes crear cajas que contengan number, string, u otros tipos.

Genéricos en Interfaces

También puedes usar genéricos en interfaces para describir estructuras de datos más complejas:

Aquí, Pair<T, U> es una interfaz genérica que define una estructura de datos que contiene dos propiedades, first de tipo T y second de tipo U.

Restringiendo a tipos con una propiedad específica

Supongamos que quieres crear una función que opere sobre objetos que tengan una propiedad length. Puedes usar extends para asegurarte de que solo se puedan pasar objetos que tengan esa propiedad.

En este ejemplo:

  • La función logLength acepta un argumento value de tipo T.

  • El tipo T está restringido a tipos que tienen una propiedad length de tipo number.

  • Intentar pasar un valor que no cumpla con esta restricción resultará en un error de compilación.

Ejemplo 2: Restringiendo a tipos derivados de una interfaz

Puedes usar extends para limitar los tipos genéricos a aquellos que extienden de una interfaz específica. Esto asegura que los tipos genéricos tengan ciertas propiedades y métodos definidos en esa interfaz.

En este ejemplo:

  • La función greet solo acepta argumentos que extiendan de la interfaz Person.

  • El objeto pasado a greet debe tener las propiedades name y age.

  • Si intentas pasar un objeto que no cumple con la interfaz Person, TypeScript generará un error.

Ejemplo 3: Restringiendo a tipos que extienden de una clase

Puedes también restringir tipos genéricos a aquellos que extienden de una clase específica. Esto permite que el genérico use métodos y propiedades definidos en la clase base.

En este ejemplo:

  • La función makeMove está restringida a aceptar tipos que extienden de la clase Animal.

  • Esto asegura que cualquier argumento pasado a makeMove tendrá acceso al método move.

  • Intentar pasar un objeto que no extienda de Animal resultará en un error.

Ejemplo 4: Múltiples restricciones con extends

Puedes restringir un tipo genérico para que extienda de múltiples tipos usando una intersección de tipos.

En este ejemplo:

  • La función printAndLogLength acepta un argumento item de tipo T.

  • T está restringido a tipos que extienden tanto de HasLength como de Printable.

  • Esto asegura que item tendrá tanto la propiedad length como el método print.

Declaration Files

En TypeScript, las declaration files (archivos de declaración) o simplemente definitions son archivos con la extensión .d.ts que proporcionan descripciones de los tipos presentes en archivos JavaScript. Estos archivos permiten a TypeScript conocer las formas y tipos de las entidades definidas en archivos JavaScript, lo que es especialmente útil cuando se trabaja con bibliotecas de JavaScript que no están escritas en TypeScript.

Propósito de los Declaration Files

  1. Interoperabilidad con JavaScript: Permiten que TypeScript pueda interactuar con bibliotecas y módulos de JavaScript proporcionando información de tipos.

  2. Mejora de la Experiencia de Desarrollo: Proporcionan autocompletado, documentación en línea y verificación de tipos en los editores de código.

  3. Evitan Errores en Tiempo de Desarrollo: Ayudan a detectar errores en tiempo de desarrollo al verificar los tipos de datos.

Creación de Declaration Files

Puedes crear tus propios archivos de declaración o usar los que están disponibles en la comunidad a través de DefinitelyTyped.

Ejemplo de un Archivo de Declaración

Supongamos que tienes una biblioteca JavaScript llamada myLibrary.js:// myLibrary.jsfunction greet(name) { return `Hello, ${name}!`;}

Puedes crear un archivo de declaración llamado myLibrary.d.ts para describir esta función:

Uso del Archivo de Declaración

Ahora puedes usar la función greet en un archivo TypeScript con verificación de tipos:

DefinitelyTyped y @types

DefinitelyTyped es un repositorio que contiene archivos de declaración para muchas bibliotecas populares de JavaScript. Puedes instalar estos archivos de declaración mediante npm utilizando el prefijo @types.

Ejemplo de Instalación

Para instalar los tipos para una biblioteca como lodash, puedes usar:

Esto instalará los tipos necesarios para lodash y te permitirá usar la biblioteca con verificación de tipos en TypeScript.

Ejemplo Completo

Si quisieras utilizar lodash en un proyecto TypeScript, primero instalarías tanto lodash como sus tipos:

Luego podrías usar lodash en tu código TypeScript con autocompletado y verificación de tipos:

Last updated