# 4. Buenas Prácticas en TypeScript

**TODO LO QUE HACEMOS DEBE ESTAR EN TYPESCRIPT**

Sí, TypeScript es un linter glorificado, pero es el que nos permite movernos de forma segura dentro del despelote que armó el uso desmedido de JavaScript en la web y las ganas de meterlo hasta en la cafetera. No es solo una barrera de seguridad, es una herramienta para construir aplicaciones más robustas y fáciles de entender.

## Explicar lo suficiente

TypeScript puede inferir tipos por sí solo, pero no siempre es la mejor práctica. En algunos casos, el tipado explícito es fundamental para la claridad y la legibilidad.

**Tipado inferido**: Lo usamos en casos obvios, donde el tipo es claro a simple vista (ej., `const name = 'Andrés'`). A menudo, los tipos inferidos pueden ser más precisos que los explícitos. En este caso, el tipo de `name` sería la cadena literal `'Andrés'`, que es más precisa que `string`.

**Tipado explícito**: Es esencial para funciones, propiedades de objetos o variables que podrían tener más de un tipo (ej., `const data: string[] | number[]`). También lo usamos cuando el tipo no es tan fácil de inferir, como en un objeto literal, o cuando sabemos que el valor va a ser asignado por un desarrollador, para no correr el riesgo que un cambio de tipo `42` a `"42"` nos rompa el programa. Este tipado explícito nos ayuda a documentar el código sin necesidad de más comentarios.

**Retornos Tipados**: Aunque TypeScript puede inferir muy bien el tipo de retorno, hacer explícitos los tipos del retorno en la firma de la función, ayuda a dar una idea clara de lo que hace antes de analizar su funcionamiento interno.

## Interfaces y Tipos: ¿Cuál y cuándo?

La elección entre `interface` y `type` puede ser confusa, y cada 8 días puede cambiar la forma en la que uno tiparía un objeto, pero generalmente...

**Type**: Es el la declaración base, es la más flexible y te permite crear uniones (`|`), intersecciones (`&`) y definir tipos primitivos o literales, al igual que objetos.

**Interface**: Su uso es más cercano a las Clases por su posibilidad de ser extensible, lo cual nos puede servir para crear jerarquías de tipos.

## Arrays y Genéricos

**Tipado de _arrays_**: Preferimos la sintaxis `string[]` en lugar de `Array<string>`. Es más concisa, fácil de leer y la convención más común. **NOTA**: Dentro de nuestro código aún es posible encontrar `Array<SomeType>`, es muy probable estés viendo algo viejo y que está en proceso de ser actualizado.

**Construcción de genéricos**: Úsalos para crear componentes o funciones que pueden trabajar con una variedad de tipos de datos sin perder el tipado. Un buen genérico no solo tipa la entrada, sino que también tipa la salida, asegurando que la información que retorna la función sea del mismo tipo que recibió.

```typescript
// Mal ejemplo: usa 'any' y pierde el tipado del array.
function getItem(array: any[], index: number): any {
  return array[index];
}

// Buen ejemplo: usa un tipo genérico 'T' para mantener el tipado.
// La función `getItem` siempre devolverá un elemento del mismo tipo que recibió.
function getItem<T>(array: T[], index: number): T {
  return array[index];
}

const names = ['Andrés', 'Sofía', 'Pedro'];
const aName = getItem(names, 1); // aName es de tipo string, no 'any'.
```

## Archivos de definición de tipos

Para mantener el proyecto organizado, el alcance de los tipos es crucial. Cuando un tipo tiene un alcance limitado a un solo archivo, lo declaramos en ese mismo archivo. Esto mantiene el código cohesivo.

Si un tipo se usa en múltiples archivos, lo extraemos a un archivo `*.types.ts` en la carpeta más cercana que contenga todos los puntos donde se usa. Esta práctica asegura que tengamos una única fuente de verdad, lo que mejora la consistencia y la sostenibilidad del código.

## Vamo' a calmarno'

Typescript es un linter, no vale la pena pasar más tiempo construyendo tipos perfectos que escribiendo la lógica de lo que estamos trabajando. Si una solución simple funciona, usarla sin miedo. Las abstracciones innecesarias y la "masturbación de tipos" solo añaden confusión y complican el código.

## Constantes con enums y posibilidades con literales

Para mantener el código ordenado, siempre que sea posible podemos agrupar el tipado de contantes relacionadas en enums

```typescript
enum Status {
  Loading = 'LOADING',
  Success = 'SUCCESS',
  Error = 'ERROR',
}

const currentStatus = Status.Loading;
```

El uso de literales es recomendado para un conjunto de valores fijos y acotados. Por ejemplo: `type UserRole = 'admin' | 'guest'`.

## `any`, `unknown` y `never`

**`any`**: **NO USARLO NUNCA**, salvo que estemos en proceso de migrar algo de JS a TS. Es la peor práctica y anula completamente el propósito de TypeScript.

**`unknown`**: Este es el tipo de dato que debes usar cuando no sabes qué tipo va a entrar. Después, usa _type narrowing_ para trabajar con el valor. si tienes dudas sobre _type narrowing_ esta [guía rápida sobre type narrowing](https://dev.to/hichembenchaabene/typescript-type-narrowing-a-comprehensive-guide-4kkm) es bastante completa y rápida de leer.

**`never`**: Se usa para indicar que una función **nunca** retorna un valor (ej., una función que siempre lanza un error o entra en un bucle infinito).

## Helpers y Tipos Utilitarios

**Type Guards**: Son funciones especializadas que validan y refinan el tipo de un valor en tiempo de ejecución. También son usadas en _type narrowing_ y nos sirven para retener la info del tipo en la ejecución y tienen la firma característica `(myValue: unknown): myValue is Type`, donde el valor de retorno booleano determina si TypeScript debe tratar el valor como el tipo especificado. Son fundamentales para trabajar de forma segura con datos de origen incierto, como respuestas de APIs, entrada del usuario, o cualquier valor de tipo unknown o any

```typescript
interface DemonData {
  name: string;
  powerLevel: number;
}

// Type guards 
function isCursedString(value: unknown): value is string {
  return typeof value === 'string';
}

function isDemon(value: unknown): value is DemonData {
  return typeof value === 'object' && 
         value !== null &&
         'name' in value && 
         'powerLevel' in value;
}

// Implementacion de los type guards
function summonEntity(entity: unknown) {
  if (isCursedString(entity)) {
    console.log(`Invocando: ${entity.toUpperCase()}`); // ✅ TypeScript sabe que es string
  } else if (isDemon(entity)) {
    console.log(`${entity.name} aparece con poder ${entity.powerLevel}`); // ✅ TypeScript sabe que es Demon
  }
}

// ejecución
summonEntity("BELZEBÚ"); // → "Invocando: BELZEBÚ"
summonEntity({ name: "Asmodeo", powerLevel: 666 }); // → "Asmodeo aparece con poder 666"
```
  
**`Partial`, `Pick`, `Omit`, and shit...**: Los tipos utilitarios de TypeScript son poderosos. En lugar de crear un tipo nuevo cada vez que necesitas una variante de un tipo ya existente, usar los utilitarios para crear un tipo derivado, [acá un ejemplo](https://youtube.com/shorts/4CYkhXIDxeQ?si=YwakdrVEdq2LGjxS).

---
[Volver al menú principal](../frontend-structure.md) | [Siguiente: Buenas Prácticas en React](./5-buenas-practicas-en-react.md)
