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:
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.
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.
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.
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.
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
string
: Representa una cadena de texto.number
: Representa números, ya sean enteros o de punto flotante.boolean
: Representa valores de verdad (true
ofalse
).null
yundefined
: Representan la ausencia de valor.
Type
Se usa para definir tipos de variables y generalmente se usa para tipos primitivos.
Tipos Compuestos
Arreglos (
Array
): Representa una lista de elementos del mismo tipo.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];
Tuplas (
Tuple
): Representa un arreglo con un número fijo de elementos de tipos específicos.Objetos (
Object
): Representa una colección de propiedades con valores de tipos específicos.
Tipos Especiales
any
: Desactiva la verificación de tipos, permitiendo cualquier valor. Úsalo con precaución.unknown
: Similar aany
, pero más seguro porque aún requiere una verificación de tipo antes de usarlo.Unknown type
, este tipo de dato es la mejora deany
, 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 detypeof
.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:
void
: Representa la ausencia de un valor, comúnmente utilizado en funciones que no retornan nada.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
Unión (
Union
): Permite que una variable tenga más de un tipo.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.
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 deT
sean opcionales.Required<T>
: Hace que todas las propiedades deT
sean requeridas.Readonly<T>
: Hace que todas las propiedades deT
sean de solo lectura.Pick<T, K>
: Selecciona un subconjunto de las propiedades deT
.Omit<T, K>
: Excluye un subconjunto de las propiedades deT
.
Tipado por indice
Diferencias Clave Enum, Type e Interface
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.
Herencia y Extensión:
enum
: No admite herencia.type
: No admite herencia, pero puede combinarse usandoUnion
yIntersection
.interface
: Admite la herencia múltiple y la implementación en clases.
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.
Compatibilidad:
En general,
interface
ytype
son intercambiables en muchos casos, perointerface
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
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
)
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
No Instanciables: No se pueden crear instancias directamente de una clase abstracta.
Métodos Abstractos: Pueden contener métodos sin implementación que deben ser implementados por las clases derivadas.
Métodos Concretos: Pueden contener métodos con implementación que pueden ser heredados o sobrescritos por las clases derivadas.
Propiedades Abstractas: Pueden definir propiedades abstractas que deben ser implementadas por las clases derivadas.
Ejemplo de Clase Abstracta
Interfaces (interface
) y implements
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
Contrato de Implementación: Define un conjunto de métodos y propiedades que una clase debe implementar.
Sin Implementación: No pueden contener implementaciones de métodos.
Herencia Múltiple: Una clase puede implementar múltiples interfaces.
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 usanT
,U
,V
, etc.).value: T
significa que la función tomará un argumentovalue
de cualquier tipoT
.: T
después de la firma de la función indica que el tipo de retorno de la función será del mismo tipoT
.
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 argumentovalue
de tipoT
.El tipo
T
está restringido a tipos que tienen una propiedadlength
de tiponumber
.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 interfazPerson
.El objeto pasado a
greet
debe tener las propiedadesname
yage
.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 claseAnimal
.Esto asegura que cualquier argumento pasado a
makeMove
tendrá acceso al métodomove
.Intentar pasar un objeto que no extienda de
Animal
resultará en un error.
Ejemplo 4: Múltiples restricciones con extends
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 argumentoitem
de tipoT
.T
está restringido a tipos que extienden tanto deHasLength
como dePrintable
.Esto asegura que
item
tendrá tanto la propiedadlength
como el métodoprint
.
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
Interoperabilidad con JavaScript: Permiten que TypeScript pueda interactuar con bibliotecas y módulos de JavaScript proporcionando información de tipos.
Mejora de la Experiencia de Desarrollo: Proporcionan autocompletado, documentación en línea y verificación de tipos en los editores de código.
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