# TypeScript
## Fundamentos
[Documentacion TypeScript](https://www.typescriptlang.org/docs/handbook/compiler-options.html)
## Iniciar un proyecto de ts
1. Iniciamos un proyecto de Node
```bash=
$ npm init -y
$ npm i typescript -D
```
Para transpilar codigo **ts** a **js** y mandar el resultado a otra carpeta.
```bash=
$ npx tsc src/demo.ts --target es6
$ src/*.ts --target es6 --outDir dist
```
### Configurando el tsconfig.json
Como resumen, el comando
```bash=
$ npx tsc --init
```
inicializa un archivo tsconfig.ts. En este va estar la configuración como el **target**, **ourDir**, **strictMode**, etc. Evitándonos tener que poner esas flags en cada compilación.
Una vez con ese archivo, unicamente ejecutamos el comando:
```bash=
$npx tsc
```
Y ya por ultimo, podemos evitarnos la compilación continua corriendo el comando
```bash=
$npx tsc --watch
```
### Configurando el proyecto con ts-node
[ts-node](https://typestrong.org/ts-node/) es una libreria que nos permite ejecutar archivos TypeScript de manera directa, en lugar de tener que ejecutar el archivo JavaScript transpilado. Para instalarlo ejecutamos el comando:
```typescript=
npm i -D ts-node
```
Una vez instalado podemos ejecutar archivos TypeScript de manera automática:
```bash=
$npx ts-node demo.ts
```
## Tipos de Datos Especiales
### Any
El tipo de dato **Any** nos permite tener variables de tipado dinámico, es decir, podemos hacer esto:
```typescript=
let anyVar: any = 12;
anyVar = "def";
anyVar = [];
```
No es buena práctica hacer esto, porque desactiva el analizador sintáctico de TypeScript, es como si estuviesemos usando JavaScript. Aunque podemos hacer uso de este tipo si estamos incorporando TypeScript de manera incremental.
### Union Types
Los Union Types nos permiten definir una variable que puede ser de diferentes tipos. Para hacerlo debemos colocar un tipo seguido de el símbolo **"|"** y el siguiente tipo.
```typescript=
let myVar: string | number = "Union Types";
myVar = 12;
```
### Alias y Tipos literales
Podemos crear nuestros propios tipos de la siguiente forma:
```typescript=
type MyType = string | number;
let myVar: MyType = "Mi tipo"
```
Otra cosa que podemos hacer en TypeScript es asignar tipos literales, es decir, que una variable solo pueda tomar los valores de un cierto conjunto. Por ejemplo:
```typescript=
let shirtSize "S" | "M" | "L" | "XL" = "M";
```
### Enums
El Enum es un tipo de dato que nos permite definir una serie de opciones, para que una variable de ese tipo solo pueda tomar esos valores. Ej:
```typescript=
enum SHIRT_SIZES {
S = "S",
M = "M",
L = "L",
XL = "XL",
}
```
La ventaja de esto es que podemos reutilizar ese enum en varios sitios, de tal forma que si cambiamos el valor de alguna opción no tenemos que ir modificando todos los archivos donde usamos ese enum.
### Tuplas
Las tuplas nos sirven para hacer un array fuertemente tipado especificando el tipo de dato de cada elemento del array así como la cantidad de elementos. Para hacer una tupla lo hacemos de la siguiente manera.
```typescript=
const user: [string, number, boolean] = ["Irving", 20, true]
```
No confundir las tuplas de Typescript con las de JavaScript ( que esta en stage 2)
entre varias características que las diferencian están la inmutabilidad y posibilidad de comparación que tienen las tuplas de JavaScript en cambio las tuplas de Typescript están mas enfocadas en el tipado.
### Unknown
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 tipo.
```typescript=
let foo: unknown = null;
```
### Never
Una forma de ver al tipo de dato **never** son aquellas funciones que nunca llegan a ejecutarse por completo, ya sea porque es un bucle infinito o se lanze un error. Por ejemplo:ç
```typescript=
// Funcion que retorna un Never
const whitOutEnd = () => {
while(true) {
console.log("Nunca acaba")
}
}
```
## Casteo o Transformación de Variables
En muchas ocasiones podemos tener variables tipadas con any o variables que pueden ser varios tipos. Se puede dar el caso que queramos tratar esa variabe con un tipo especifico, ya que sabemos que lo que nos llega si o si es de ese tipo.
Para poder tratar la variable con ese tipo especifíco podemos hacer un **casteo**, en TypeScript podemos hacerlo de dos formas:
```typescript=
const myVar: any = "Casting";
// Forma 1
const castingVar: string = myVar as string;
// Forma 2
const castingVAr2: string = <string>myVar;
```
## Funciones
### Parámetros opcionales y nullish-coalescing
Cuando existen parámetros opcionales pero queremos asignar un valor por defecto, generalmente hacemos uso del operador **OR** **'||'**. Este operador tiene un problema, y es que en JavaScript sucede que:
```typescript=
// 0 === false
// '' === false
```
Por lo que si le pasamos el valor 0, false o '', va a haber un error y se seleccionara el valor por defecto, cuando esto no es lo queremos.
La solución es usar el operador **nullis-coalescing** **'??'**. Este funciona igual que el operador **OR**, pero solo teniendo en cuenta los valores **null** y **undefined**. Ej:
```typescript=
const createProduct = (productName: string, stock?:number, isNew?:boolean) => {
return {
productName,
stock: stock ?? 10,
isNew: isNew ?? true
}
};
console.log(createProduct("Papas",0,false));
// Con '??' ==> { productName: 'Papas', stock: 0, isNew: false }
// Con '||' ==> { productName: 'Papas', stock: 0, isNew: true }
```
### Parámetros por Defecto
En TypeScript podemos dar valores por defecto a los parámetros de las funciones en caso de que no se las pasemos. Ej:
```typescript=
const createProduct = (productName: string, stock:number = 10, isNew:boolean = true) => {
return {
productName,
stock: stock,
isNew: isNew
}
};
console.log(createProduct("Papas"));
```
### Parámetros REST
En JavaScript podemos pasar argumentos infinitos a una función de esta forma:
```javascript=
const func = (...args) => {
console.log(args);
}
func(1,2,3,4,4,5,6,6,6) // [1,2,3,4,4,5,6,6,6]
```
Esto es el ***rest params***, si nos damos cuenta esto nos muestra los parámetros que le mandemos en forma de ***Array***. Como podemos aprovechar está funcionalidad en TypeScript? De la siguiente forma:
```typescript=
const func = (...args: number[]) => {
console.log(args);
}
func(1,2,3,4,4,5,6,6,6) // [1,2,3,4,4,5,6,6,6]
```
Si queremos recibir parámetros de varios tipos podemos hacerlo de la siguiente manera:
```typescript=
const func = (...args: (string | number)[]) => {
console.log(args);
};
func(1,2,3,4,4,5,6,6,6, "string") // [1,2,3,4,4,5,6,6,6,'string']
```
### Sobrecarga de Funciones
Cuando tenemos una función que puede retornar varios tipos de datos, pero sabemos que en un caso concreta nos va a retornar una **string**, TypeScript no nos va a saber indentificar si eso es una **string** o no, veámos el siguiente ejemplo:
```typescript=
const isArrayOrString = (op: string | string []): string | string[] => {
if(Array.isArray(op)) {
return "Is an array";
}
return ["Is an array"]
};
const res = isArrayOrString("fewf") // El tipo inferido será string | string[]
```
En este caso si sabemos que lo que nos devuelve es un array y queremos hacer un ***.map()*** no vamos a poder, tendríamos que comprobar que esa variable es un array.
Para esto existe la sobrecarga de funciones, donde declaramos varias veces la función sobrecargando el tipo que puede devolver. Es **importante** saber que la sobrecarga **no funciona con arrow functions**, debemos declarar este tipo de funciones con la palabra reservada ***function***. Ej:
```typescript=
export function overLoad(op:string):string[];
export function overLoad(op:string[]):string;
export function overLoad(op:unknown): unknown {
if(Array.isArray(op)) {
return "Is an Array"
}
return ["Is an Array"]
};
const isArray = overLoad("def"); // ["Is an Array"]
const isString = overLoad(["def"]); // "Is an Array"
```
Es **importante** también poner a palabra **export** para que no haya errores de redeclaración.
## Interfaces
### type VS interface
Podemos declarar nuestros tipos con la palabra reservada ***interface***, de igual forma que hacíamos con ***type***. Con la diferencia que declarandolo como ***interface*** podemos crear nuevos tipos extendiendo a otros, por ejemplo:
```typescript=
interface Person {
name: string,
age: number,
sex: 'H' | 'M'
}
interface SuperHeroe extends Person {
power: string
}
const hulk: SuperHeroe = {
name: "Bruce Banner",
age: 45,
sex: 'H',
power: 'Super Strength'
};
console.log(hulk)
// { name: 'Bruce Banner', age: 45, sex: 'H', power: 'Super Strength' }
```
Podemos usar ***type*** en lugar de ***interface*** cuando tenemos tipos sencillo como:
```typescript=
type MyType = string | number;
```
Ya que nos permiten generar un tipo de una forma más sencilla, se podría hacer como una interfaz, pero habría que crear un cuerpo y se ve mucho más engorroso.
### readonly
Como su nombre lo indica, este feature de TS nos ayuda a tener ciertos **atributos solo de lectura**. Lo que significa que no pueden ser modificados. Ej:
```typescript=
interface BaseModel {
readonly id: string | number;
readonly createdAt: Date;
updatedAt: Date;
}
```
### Utility Types
#### Omit y Pick type
Estos tipos de datos nos permiten crear nuevas interfaces basadas de otras, pero omitiendo o seleccionando solo ciertos valores. Estos funcionan de la siguiente forma.
```typescript=
interface Employee {
name: string;
lastName: string;
salary: number;
company: string
}
// Con interface
interface Unemployee extends Omit<Employee, 'salary' | 'company'> {};
// Con type
type Unemployee = Omit<Employee, 'salary' | 'company'>;
```
Con Pick funciona de la misma manera, solo que se eligen ciertos valores en vez de omitirlos.
```typescript=
interface Employee {
name: string;
lastName: string;
salary: number;
company: string
};
// Con interface
interface Company extends Pick<Employee, 'company'> {};
// Con type
type Company = Pick<Employee, 'company'>;
```
#### Partial y Required Types
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>;
```
De esa forma en ***UpdateProduct***, todos los valores de ***Product*** van a ser opcionales y a la inversa en el caso de ***StrictProduct***.
#### ReadonlyType
Este tipo de dato nos permite crear un tipo en el que todos sus valores sean solo de lectura, para proteger nuestros datos en caso de que solo queramos buscar, por ejemplo.
```typescript=
interface Product {
title: string;
price: number;
category: string;
size?: string;
};
type FindProduct = Readonly<Product>
```
Podemos combinar varios UtilityTypes, por ejemplo si queremos que el FindProduct tenga parámetros opcionales podemos combinarlo con un Partial.
```typescript=
interface Product {
title: string;
price: number;
category: string;
size?: string;
};
type FindProduct = Readonly<Partial<Product>>
```
#### Acceder al tipado por Indice
Como acceder al tipado de cierta propiedad?
Lo hacemos de una manera muy similar a la cual accedemos a valores en arrays dentro de JS, pero en este caso, en lugar de hacerlo en un array, lo aplicamos a una interfaz.
```typescript=
interface Human {
name: string;
age: number;
isAlien: boolean;
}
function createHuman(name: Human["name"]) {
// code
}
```
En este caso, el parámetro ***name*** en la función ***createHuman*** seria un string. La ventaja de esto es que si nosotros cambiamos ese tipo de dato por cualquier otra cosa, se cambia en automático.
#### ReadonlyArray
Este tipo de dato es un array, pero la principal diferencia con los arrays “normales” es que no existen ninguno de los métodos de mutación, tales como pop, push, shift, etc.
```typescript=
cont arr: ReadonlyArray<number> = [1,2,3,4,5]
```
En caso de tratar de usar alguno de estos metodos nos arroja un error
## Enlaces Útiles
[Generador de GitIgnore](https://www.toptal.com/developers/gitignore)
[faker: Libreria para crear mock de datos](https://fakerjs.dev/)
###### tags: `Platzi`