# 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](https://en.wikipedia.org/wiki/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

<figure><img src="https://624742151-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFRKN8coRWwuuSr24p99X%2Fuploads%2FDCw5wKNGrfp4RA5pIDXa%2Fimage.png?alt=media&#x26;token=416f3ec9-dff3-4efb-a4fe-e5dbfc694aa2" alt=""><figcaption></figcaption></figure>

### Tipos Primitivos

1. **`string`**: Representa una cadena de texto.

   ```typescript
   let name: string = "John Doe";
   ```
2. **`number`**: Representa números, ya sean enteros o de punto flotante.

   ```typescript
   let age: number = 30;
   ```
3. **`boolean`**: Representa valores de verdad (`true` o `false`).

   ```typescript
   let isActive: boolean = true;
   ```
4. **`null` y `undefined`**: Representan la ausencia de valor.

   ```typescript
   let value: null = null;
   let notAssigned: undefined = undefined;
   ```

### Type

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

```typescript
type UserID = string | number | boolean;
```

#### Tipos Compuestos

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

   ```typescript
   let numbers: number[] = [1, 2, 3];
   // o
   let strings: Array<string> = ["a", "b", "c"];
   ```

   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.

   ```typescript
   let person: [string, number] = ["Alice", 25];
   ```
3. **Objetos (`Object`)**: Representa una colección de propiedades con valores de tipos específicos.

   ```typescript
   let person: { name: string; age: number } = {  name: "Bob",  age: 40};
   ```

#### 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.

```typescript
typescriptCopiar códigolet numbers: number[] = [1, 2, 3]; // ✅ Correcto
numbers = []; // ✅ Correcto (arreglo vacío)
numbers = [4, 5, 6, 7]; // ✅ Correcto (cualquier longitud)
numbers = [1, "two", 3]; // ❌ Error: "two" no es un número
```

***

#### **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.

```typescript
typescriptCopiar códigolet numbers: [number] = [1]; // ✅ Correcto
numbers = []; // ❌ Error: Se esperaba 1 elemento
numbers = [1, 2]; // ❌ Error: Se esperaba solo 1 elemento
numbers = ["one"]; // ❌ Error: El elemento no es un número
```

***

#### Tipos Especiales

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

   ```typescript
   let anything: any = "Hello";
   anything = 42;
   ```

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

   ```typescript
   let notSure: unknown = 4;
   if (typeof notSure === "number") {  
       let num: number = notSure; 
       // Necesita verificación
   }
   ```

   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:&#x20;

      `Array.isArray(input);`

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

      ```typescript
      class ValueService { ... }
      class FakeValueService { ... }
      class MasterService {
        constructor(
          private valueService: ValueService
        ) { }
        ...
      }
      ...
      const fakeValueService = FakeValueService();
      const masterService = new MasterService(fakeValueService as unknown as ValueService);
      ```

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

   ```typescript
   function logMessage(message: string): void {  console.log(message);}
   ```

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.

   ```typescript
   function error(message: string): never {  throw new Error(message);}
   ```

### 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.

```tsx
(variableName as dataType)
```

#### Angle bracket

Es lo mismo que `as` pero con otra sintaxis.

```html
(<dataType>variableName)
```

```typescript
let x: unknown = 'hello'; 
console.log((x as string).length);
console.log(<string>x).length);
```

#### Tipos Literales

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

```typescript
let direction: "north" | "south" | "east" | "west";
direction = "north"; 
// Correctodirection = "up"; 
// Error: Type '"up"' is not assignable to type '"north" | "south" | "east" | "west"'.
```

#### Tipos Unión y Tipos Intersección

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

   ```typescript
   let id: number | string;
   id = 101;
   id = "202";
   ```
2. **Intersección (`Intersection`)**: Combina múltiples tipos en uno solo.

   ```typescript
   interface Person {  name: string;}
   interface Employee {  employeeId: number;}
   type EmployeePerson = Person & Employee;
   let employee: EmployeePerson = {  
       name: "John",  employeeId: 123
   };
   ```

#### Tipos de Funciones

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

```typescript
function add(a: number, b: number): number {  return a + b;}
```

## Enum&#x20;

Set de conjunto de opciones&#x20;

```typescript
enum Estaciones { 
    primavera = "Primavera", 
    verano = "Verano", 
    otonio = "Otoño", 
    invierno = "Invierno", 
}
```

## Sobrecarga de funciones

```typescript
type customType = string | string[];

function parseStr(arg: string): string[]
function parseStr(arg: string[]): string
// any é unknown, siempre van al final
function parseStr(arg: any): customType {
    // code here...
}
```

## Interfaces&#x20;

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

```typescript
interface EmployeeModel {
  id: number;
  name: string;
  lastName: string;
  salary: number;
  company: {
    id: number;
     name: string;
  }	
}
```

**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.&#x20;

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:

```typescript
interface EmployeeModelDto extends Omit<EmployeeModel, 'id' | 'name'> {
  companyId: number;
}
```

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á:

```typescript
interface EmployeeModelDto {
  lastName: string;
  salary: number;
  company: {
    id: number;
    name: string;
  };
  companyId: number;
}
```

***

#### **2. `Pick`**

* Se utiliza para **seleccionar** una o más propiedades de un tipo o interfaz.
* En tu ejemplo:

```typescript
interface EmployeeModelDto extends Pick<EmployeeModel, 'id' | 'name'> {
  companyId: number;
}
```

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á:

```typescript
interface EmployeeModelDto {
  id: number;
  name: string;
  companyId: number;
}
```

***

#### **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.

```typescript
interface Product {
  title: string;
  price: number;
  category: string;
  size?: string;
}

type UpdateProduct = Partial<Product>
type StrictProduct = Required<Product>
```

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

<pre class="language-typescript"><code class="lang-typescript">type TypeName = Readonly&#x3C;SomeType>;

<strong>interface Example {
</strong>  readonly property1: string;
  property2: number;
}

interface Example {
  property1: Readonly&#x3C;string>;
  property2: number;
}

interface InterfaceName extends Readonly&#x3C;OtherInterface> {
  // statements
}
</code></pre>

#### 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`.

```typescript
interface Person {  name: string;  age?: number;}
type PartialPerson = Partial<Person>;
type RequiredPerson = Required<Person>;
type ReadonlyPerson = Readonly<Person>;
type NameOnly = Pick<Person, "name">;
type AgeExcluded = Omit<Person, "age">;
```

## Tipado por indice

```typescript
interface Human {
  name: string;
  age: number;
  isAlien: boolean;
}

function createHuman(name: Human["name"]) {
  // code
}
```

### 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)

```typescript
constructor (
  public year: number = 1993,
  public month: number = 7,
  private day: number = 3
) {}
```

* 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.&#x20;
* 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`.&#x20;

**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**

```typescript
abstract class Animal {  
    constructor(public name: string) {}  
    // Método abstracto: debe ser implementado por las clases derivadas  
    abstract makeSound(): void;  
    // Método concreto: puede ser heredado o sobrescrito  
    move(): void {    console.log(`${this.name} is moving.`);  }
}

class Dog extends Animal {  
    constructor(name: string) {    
        super(name);  
    }  
    // Implementación del método abstracto  
    makeSound(): void {    
        console.log(`${this.name} says: Woof!`);  
    }
}
    
const myDog = new Dog("Buddy");
myDog.makeSound(); // Buddy says: Woof!
myDog.move(); // Buddy is moving.
```

#### 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`**

```typescript
interface Flyable {  
    fly(): void;
}
interface Swimmable {  
    swim(): void;
}
class Bird implements Flyable {  
    constructor(public name: string) {}  
    // Implementación del método de la interfaz Flyable  
    fly(): void {    
        console.log(`${this.name} is flying.`);  
    }
}
class Fish implements Swimmable {  
    constructor(public name: string) {}  
    // Implementación del método de la interfaz Swimmable  
    swim(): void {    
        console.log(`${this.name} is swimming.`);  
    }
}
class Duck implements Flyable, Swimmable {  
    constructor(public name: string) {}  
    // Implementación de los métodos de las interfaces Flyable y Swimmable  
    fly(): void {    
        console.log(`${this.name} is flying.`);  
    }      
    swim(): void {    
        console.log(`${this.name} is swimming.`);  
    }
}
const myBird = new Bird("Parrot");
myBird.fly(); // Parrot is flying.
const myFish = new Fish("Goldfish");
myFish.swim(); // Goldfish is swimming.
const myDuck = new Duck("Donald");
myDuck.fly(); // Donald is flying.
myDuck.swim(); // Donald is swimming.
```

#### 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**

```typescript
interface Drivable {  
    drive(): void;
}
abstract class Vehicle implements Drivable {  
    constructor(public brand: string) {}  
    // Método abstracto: debe ser implementado por las clases derivadas  
    abstract startEngine(): void;  
    // Implementación del método de la interfaz Drivable  
    drive(): void {    
        console.log(`${this.brand} is driving.`);  
    }
}
class Car extends Vehicle {  
    constructor(brand: string) {    
        super(brand); 
    }  
    // Implementación del método abstracto  
    startEngine(): void {    
        console.log(`${this.brand} engine started.`);  
    }
}
const myCar = new Car("Toyota");
myCar.startEngine();// Toyota engine started.
myCar.drive(); // Toyota is driving.
```

#### 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.

```typescript
export class MyService {
    static instance: MyService | null = null;

    private constructor(
      private name: string
    ) {}

    get Name() {
      return this.name;
    }

    static create(name: string) {
      if (MyService.instance === null) {
        console.log('Creating new instance');
        MyService.instance = new MyService(name);
      }
      return MyService.instance;
    }
  }

const myService1 = MyService.create('MyService1');
const myService2 = MyService.create('MyService2');
console.log(myService1 === myService2); //* true
```

## Genéricos&#x20;

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:

```typescript
function identity<T>(value: T): T {
    return value;
}
```

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:

```typescript
let num = identity<number>(42); // num es de tipo number
let str = identity<string>("Hello"); // str es de tipo string
let bool = identity<boolean>(true); // bool es de tipo boolean
```

#### 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:

```typescript
let num = identity(42); // TypeScript infiere que T es number
let str = identity("Hello"); // TypeScript infiere que T es string
```

#### Genéricos en Clases

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

```typescript
class Box<T> {
    private contents: T;

    constructor(value: T) {
        this.contents = value;
    }

    getContents(): T {
        return this.contents;
    }
}

const numberBox = new Box<number>(123);
console.log(numberBox.getContents()); // 123

const stringBox = new Box<string>("TypeScript");
console.log(stringBox.getContents()); // "TypeScript"
```

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:

```typescript
interface Pair<T, U> {
    first: T;
    second: U;
}

let pair: Pair<number, string> = {
    first: 42,
    second: "The answer"
};

console.log(pair.first); // 42
console.log(pair.second); // "The answer"
```

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.

```typescript
function logLength<T extends { length: number }>(value: T): void {
    console.log(value.length);
}

logLength("Hello, world!"); // 13 - Funciona porque los strings tienen 'length'
logLength([1, 2, 3, 4, 5]); // 5 - Funciona porque los arrays tienen 'length'
logLength({ length: 10 }); // 10 - Funciona porque el objeto tiene una propiedad 'length'

// logLength(42); // Error: 'number' no tiene la propiedad 'length'
// logLength({ size: 10 }); // Error: El objeto no tiene la propiedad 'length'
```

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.

```typescript
interface Person {
    name: string;
    age: number;
}

function greet<T extends Person>(person: T): void {
    console.log(`Hello, ${person.name}. You are ${person.age} years old.`);
}

const john = { name: "John", age: 30 };
greet(john); // Funciona porque 'john' cumple con la interfaz 'Person'

const jane = { name: "Jane", age: 25, job: "Engineer" };
greet(jane); // Funciona porque 'jane' cumple con la interfaz 'Person', aunque tiene propiedades adicionales

// const notAPerson = { job: "Doctor" };
// greet(notAPerson); // Error: 'notAPerson' no tiene las propiedades 'name' y 'age'
```

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.

```typescript
class Animal {
    constructor(public name: string) {}
    move(distance: number) {
        console.log(`${this.name} moved ${distance} meters.`);
    }
}

class Dog extends Animal {
    bark() {
        console.log("Woof! Woof!");
    }
}

function makeMove<T extends Animal>(animal: T, distance: number): void {
    animal.move(distance);
}

const dog = new Dog("Rex");
makeMove(dog, 10); // "Rex moved 10 meters."

// const car = { model: "Toyota", move: (distance: number) => console.log(`Car moved ${distance} meters.`) };
// makeMove(car, 20); // Error: 'car' no extiende de 'Animal'
```

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.

```typescript
interface HasLength {
    length: number;
}

interface Printable {
    print(): void;
}

function printAndLogLength<T extends HasLength & Printable>(item: T): void {
    console.log(item.length);
    item.print();
}

const book = {
    length: 500,
    print: () => console.log("Printing book..."),
};

printAndLogLength(book); // Funciona: book tiene 'length' y 'print'

// const nonPrintable = { length: 10 };
// printAndLogLength(nonPrintable); // Error: no tiene 'print'
```

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:

```typescript
// myLibrary.d.ts
declare function greet(name: string): string;
```

**Uso del Archivo de Declaración**

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

```typescript
// main.ts
/// <reference path="./myLibrary.d.ts" />
greet("World"); 
// Correctogreet(42); 
// Error: Argument of type 'number' is not assignable to parameter of type 'string'.
```

#### 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:

```bash
npm install --save-dev @types/lodash
```

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:

```bash
npm install lodashnpm install --save-dev @types/lodash
```

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

```typescript
// main.ts
import * as _ from 'lodash';
const result = _.chunk(['a', 'b', 'c', 'd'], 2);
console.log(result); // [['a', 'b'], ['c', 'd']]
```
