TYPESCRIPT
-Es un superconjunto tipado de JavaScript
-El codigo que se escribe al final se ejecuta como JavaScript pero
TypeScript ayuda antes de ejecutar a detectar errores (en el editor y al
compilar)
VENTAJAS
-Permite especificar los tipos de los variables, parametros, valores de
retorno. Permite realizar comprobaciones durante la compilaciones y
detectar errores posibles cuando se escribe el codigo
-(Opinion personal) La principal ventaja es el autocompletado que vamos a
tener con VS
POTIPS
-Cuando pasamos algo a una funcion, si no definimos el tipo nos puede
arrojar una advertencia de que lo que estamos pasando pueda ser null,
entonces para eso podemos poner !, con esto garantizamos que lo que
estamos pasando no es null
-TypeScrip puede inferir en el tipo de nuestros datos, pero es mejor
crear grupos o interfaces
TEMA 1 - TIPOS PRIMITIVOS
-Son los tipos de datos de TS soporta de forma nativa como: number,
string, boolean, null, undefined
let const : string = "ciudad" //Ya despues si queremos modificar su valor
con otro valor de diferente tipo nos saldra error, TS infiere tipo
entonces en este caso no es necesario poner el tipo
-En JavaScript cualquier numero es un number
-undefined significa que no tiene aun valor asignado
-null significa que intencionalmente no tiene valor
LITERAL TYPE
-Con let TS inferiere un tipo generalmente primitivo, con un const este
no es el caso. EJM:
const estado = "activo"
-TS infiere el tipo como "activo" no string
UNION
-Es normal poner string | null, es decir que puede tener uno u otro tipo
BIGINT
let montoGrante : bigint = 100000000000n
TEMA 2 - INFERENCIAS
OBJETOS
const user = {
id: 1,
name: "Ana",
active: true
}
El tipo es: {id: number, name: string, active: boolean}
Por tanto [Link]="1" -> error
ARRAYS
const numbers = [1, 2 ,3]
tipo -> number[]
por tanto [Link]("1") //error
FUNCTIONS
-No existe inferencia aca, se debe tipar los parametros
-En Return si se infiere -> (): number
Regla de oro -> Dejar que TS infiera cuando pueda, y especificar cuando
no pueda o hay ambiguedad
TEMA 3 - TIPADO DE FUNCIONES
-Significa decir que tipo de datos recibe y que tipo de datos retorna
-Si no tipamos, los parametros son any, entonces -> sum("hola", true) ->
ERROR
-TypeScript infiera el tipo de retorno asi que es opcional tiparlo,
pero... ser explicito puede dar claridad y mantenimiento en un caso por
ejemplo donde se retorna lo de un fetch
Parametros opcionales
function greet(name?: string)
tipo: string | undefined
Tipar funciones flecha
const handleSubmit = (url: string) : void => fetch(url)
-Podemos tiparlo como variable
const handleSubmit : (url: string) => void = (url)=>fetch(url)
function login (email: string, password: string): boolean{
}
TEMA 4 - ARRAYS TIPADOS
-Es un array donde todos sus elementos tienen un tipo definido
-Con esto, TS cuida las cosas que puedes introducir en el
-Hay 2 formas de tiparlo:
FORMA 1: const numbers : number[] = [1,2,3]; //Mas usada
FORMA 2: const numbers : Array<number> = [1, 2, 3];
-Si se inicializa el array con valores, se infiere el tipo, pero si lo
iniciamos vacio -> any[] lo cual rompe la seguridad de tipos
-Mejor -> const saludos : string[] = [];
Arrays con objetos
interface User {
name: string,
city: string,
}
const users: User[] = [{}, {}]
-Ahora TS valida cada objeto del array
-Si bien TS infiere el tipo segun los valores, es mejor tiparlo desde un
inicio debido a que mantiene una estructura, es decir que si aumentamos
objetos, entonces se validara que dichos objetos cumplan la estructura
Arrays con Union Types
-Cuando un array puede tener mas de un tipo
const randoms : (string | number)[] = ["hola", 1, "gato"]
Arrays inmutables (solo lectura)
-readonly evita mutaciones, debe ir siempre antes de los tipos
const role : readonly string[] : ["admin", "user"];
-Por tanto no permite -> [Link]("invited"); X
TEMA 5 - OBJETOS TIPADOS
-Tipar un objeto significa definir exactamente que propiedades tiene y de
que tipo es cada una
-Formas de tipar:
Tipar inline
const user: {
name:string,
edad:number,
city?:string
} = {
name: "john",
number: 3
}
-Podemos poner una propiedad opcional con ?, equivale a city:string |
undefined
-Con readonly hacemos que el objeto sea inmutable
Objetos como parametros
function printUser (user : {name: string, old: number}, saludo:string) {
}
-Funciona pero no escala bien, para eso estan las interfaces
Objetos anidados
const order: {
id: number;
customer: {
name: string;
address: string;
};
} = {
id: 1,
customer: {
name: "Carlos",
address: "Av. Siempre Viva"
}
};
-Los errores comunes que nos ayuda a proteger tipar objetos es el exceso
de propiedades (datos inesperados)
FASE 2
TEMA 1: INTERFACE
-Es una forma de describir o definir la estructura de un objeto para
poder:
[Link]
[Link] nombre
[Link] en muchos lugares sin repetir codigo
-Se debe pensar una interface como un contrado (Cualquier objeto que diga
que es de este tipo, debe cumplir esta estructura)
Interfaces con propiedades null
-Cuando el dato existe pero puede ser vacio
interface Product {
id: number,
name: string,
discount: number | null
}
-Esto es distinto de ?, ? -> puede no venir, | null viene pero puede ser
null
Intefaces anidadas
inteface Address {
street:string,
city:string
}
interface User{
address:Address;
}
Readonly en interfaces
-Muy usado para IDs, datos de backend, props
interface User {
readonly id:number;
name:string;
}
[Link] = 5;
Errores comunes
-Usar interfaces para valores primitivos
-Usar any en interfaces
TEMA 2 - TYPE VS INTERFACE
-Ambos sirven para tipar objetos pero no son lo mismo
interface -> define la forma de un objeto
type -> crea un alias de tipo (puede ser muchas cosa)
Interface
-Ideal para objetos, props de React, Modelo de datos, APIs
Type
-Type permite Union Types |
type Status = "loading" | "success" | "error" //Esto no se permite con
interfaces
-Tambien permite Aliases:
type ID = string | number;
Extension o herencia
-Ambos son iguales pero interface es mas legible
inteface Admin extends User{}
type Admin = User & {}
-Type no permite hacer declaration mergin, es decir:
interface User{
id:number;
}
intefaces User{
name:string;
}
-Tenemos como resultado
interfaces User{
id:number;
name:string;
}
Regla practica
interfaces -> Objetos / props / modelos
type -> Unions / aliases / estados
EJEMPLO REAL:
interface User {
id: number;
name: string;
}
type UserStatus = "active" | "inactive";
TEMA 3 - UNION TYPES
-Un union type significa "esto puede ser uno u otro tipo", se usa con |
(OR logico de tipos)
let user : User | null = null
-Pero tiene sus limitaciones, por ejemplo:
function printId(id: number | string){
[Link](); //error
}
-Debemos de hacer validaciones con typeof si vamos a usar funciones que
solo funcionan en uno u otro tipo (esto se llama narrowing)
function printId(id: string | number) {
if (typeof id === "string") {
[Link]([Link]());
} else {
[Link]([Link](2));
}
}
Union en objetos
inteface ApiResponde {
data: User | null;
error: string | null;
}
-Esto nos ayuda a manejar exito y error
Discriminated Unions
-El siguiente patron es muy usado en React
type Result =
| {status: "success"; data: string}
| {status: "error"; message: string}
-Esto le dice a TS, Si status === "success" -> data existe
-Un ejemplo real de esto es lo siguiente:
type AuthState =
| { status: "logged_out" }
| { status: "logged_in"; userId: number };
-Esto es correcto pero lo siguiente no lo es:
interface AuthState {
status: "logged_out" | "logged_in";
userId?: number;
}
function handle(state: AuthState) {
if ([Link] === "logged_in") {
[Link](); // error
}
}
-En aca TS dice que use Id puede ser undefined ya que userId es opcion y
no existe ninguna relacion con el status "logged_in" como con el type
-Entonces cuando una propiedad depende de otra, usar Discriminated
Unions, no propiedades opcionales
TEMA 4 - PROPIEDADES OPCIONALES
Propiedad opcional ?
email?: string // Equivalente a :string | undefined
NULL
email: string | null
-Estamos diciendo que email siempre existe pero su valor puede ser null
COMBINARLOS
inteface User {
email?: string | null;
}
-Esto sucede mucho en apis mal diseñadas
-Cuando evaluamos un undefined en un contexto de boolean, entonces
obtemos un valor fasly (falso), por tanto cuando lo negamos con !,
obtenemos un true
ERROR COMUN
-Usar ? en lugar de null
interface Product {
discount?:number;
}
-Si el backend envia null, entonces habra un error
-Si el backend envia
interface Profile {
id:number;
bio?:string;
avatar: string | null;
}
FASE 3
TEMA 1 - TIPADO DE PROPS EN REACT
-Las props son datos que el component recibe deste fuera
-Se tipan las props para que el componente reciba solo lo que espera, si
hay alguna error, autocompletado
Forma correcta de tipar props
-Normalmente se tipan con interfaces (regla practica)
inteface ButtonProps {
label: string;
}
function Button ({label} : ButtonProps) {
return ...
}
Props con funciones (Muy comun)
interface ButtonProps {
label:string;
type: "success" | "error" | "warning";
onClick: (id:number) => void;
}
Props readonly (Buena practica)
-Los props no deben mutarse, si intentas modificar, TS te avisa
-Hay 2 formas de tipar props
const Card: [Link]<CardProps> = ({}) => {} // tipar la variable
-Hoy en dia no se recomiento usar [Link], sinos una forma mas moderna:
function Card({}:CardProps ){}
TEMA 2 - CHILDREN - [Link]
-children es todo lo que colocas entre las etiquetas de un componente
-Se lo tipa de la siguiente manera:
children: [Link];
-Es un tipo que representa todo lo que React puede renderizar, por
ejemplo: string, number, JSX, arrays de JSX, null, undifined, bool,
fragments
-Si el componente puede no tener contendio: children?: [Link];
-children: [Link]; Estaria mal ya que no permite string, array,
null, etc
TEMA 3 - USESTATE CON TYPESCRIPT
Caso 1: useState Simple (inferencia automatica)
const [count, setCount] = useState(0); // count : number
-Si el valor inicial es claro -> Dejar que infiera
Caso 2: Null
const [user, setUser] = useState<User | null>(null);
-Muy comun en apps reales
Caso 3: Arrays
const [users, setUsers] = useState([]); // Mal any[]
const [users, setUsers] = useState<User[]>[]);
-Se debe especificar explicitamente el tipo
Caso 4: Union State (Muy comun)
const [state, setState] = useState<"loading" | "error" |
"warning">("loading");
TEMA 4 - TIPADO DE EVENTOS EN REACT (MUY IMPORTANTE)
- Esto se usa todo el tiempo en: Forms, Inputs, Botones, Selects,
Textareas;
-Aca mucha gente termina usando any
-Un evento es una accion o suceso que ocurre en el navegador y que puede
ser detectado y manejado por el codigo
-React no usa directamente los eventos del DOM, sinos una capa propia, es
por eso que los eventos cambian un poco con el JS o HTML normal
EVENTOS COMUNES
ONCHANGE:
const handleChange = (e: [Link]<HTMLInputElement>) =>{}
-Es un evento de cambio, proviene de un input
const handleChange = (e: [Link]<HTMLTextAreaElement>) => {}
ONCLICK
const handleClick = (e: [Link]<HTMLButtonElement>) => {}
SUBMIT
const handleSubmit = (e: [Link]<HTMLFormElement>) => {}
-Quiza algo importante para acordarse es por ejemplo:
[Link]<OrigenEtiqueta>
-TS puede inferir el tipo del parametro e de la funcion pero unicamente
cuando no separamos la funcion (esta inline)
<input onChange={(e)=>{[Link]()}}/>
const handleClick = (e: [Link]<HTMLButtonElement>) => {}
-Como consejo se mejor usar currentTarget y no target porque target puede
ser un hijo interno y currentTarget siempre es el elemento donde esta el
handler
FASE 4
TEMA 1 - TIPADO DE RESPUESTAS DE API
const response = await fetch("");
const data = await [Link]();
-Cuando hacemos esto en react, data es de tipo any
Entonces haremos los siguientes pasos:
1. Modelar la respuesta
-Primero definimos el modelo o la interface
2. Tipamos la respuesta
const data : User = await [Link]();
const data : User[] = await [Link](); (Muy comun)
CASO PROFESIONAL (Response wrapper)
-Muchas APIs devuelven algo asi:
{
"data": [...],
"message": "ok",
"success": true
}
-Por tanto el modelo seria:
interface ApiResponse<T>{
data: T | null; //Null en caso de que el backend lo devuelva
message:string;
success: boolean;
}
-Y usamos de la siguiente forma:
const result : ApiResponse<User[]> = await [Link]();
-Y ahora [Link] // [Link]
TYPE ASSERTION
-Es decirle a TS: "Confia en mi. Yo se que tipo es esto" aunque TS no
pueda comprobarlo
-Es una forma de decirle a TS que trate un valor como un tipo especifico
const inputElement: HTMLInputElement | null =
[Link]('email')
const inputElement = [Link]('email') as
HTMLInputElement;
- Con el primer codigo se le dice a TS "Esta variable debe ser de este
tipo" pero saldra un error, ya que lo que retorna el getElementById es un
tipo generico HTMLElement | null
-En ese sentido hay veces donde el codigo retorna tipos genericos,
podemos usar esos tipos genericos pero perderemos la posibilidad de usar
las propiedades de los tipos especificos
-Es ahi donde entra el as. Si estamos seguros de lo que retorna algo,
podemos usar el type assertion para poder usar las propiedades
especificas
-Esta es una propiedad muy peligroso, pero se lo puede usar en el caso de
arriba y donde TS no pueda inferir el tipo correctamente
TEMA 2 - DTOs (Frontend vs Backend)
-DTO = Data Transfer Object
-Es un objeto que se usa para transportar datos entre diferentes partes
de una aplicacion o entre sistemas (frontend <-> backend, API <-> Base de
datos)
-El DTO es la estructura de datos que viaja entre dos capas
BENEFICIOS
-Seguridad: No se expone datos sensibles
-Desacoplamiento: Frontend y backend pueden tener estructuras diferentes
-Claridad: Se define exactamente que datos se transfieren
EL PROBLEMA:
-Supongamos que el backend retorna:
{
"id": 1,
"nombre_completo": "Juan Pérez",
"fecha_creacion": "2025-01-01T00:00:00.000Z"
}
-Pero el frontend trabaja o quiere trabajar asi:
{
id: number;
fullName: string;
createdAt: Date;
}
-Ahi es donde entra los DTOs
Paso 1: Modelar el DTO del backend
-Esto representa lo que exactamente viene del backend
interface UserDTO{
id:number;
nombre_completo:string;
}
Paso 2: Modelar el modelo interno
interface User {
id:number:
fullName: string;
}
Paso 3: Transformar
function mapUser(dto: UserDTO): User {
return {
id: [Link],
fullName: dto.nombre_completo,
createdAt: new Date(dto.fecha_creacion)
};
}
USO REAL:
const response = await fetch("/api/user/1");
const dto: UserDTO = await [Link]();
const user = mapUser(dto);
POR QUE NO USAR DIRECTAMENTE EL DTO?
-El backend puede cambiar de nombres
-Backend puede enviar strings donde tu quieres Date
-Backend puede usar snake_case
-Evita sobre todo acoplamiento fuerte
-Nunca debemos confiar en el backend como modelo interno
-Si el backend cambia algo, la UI explota
Tambien debemos realizar el proceso inverso
-El modelo interno puede NO coincidir con lo que el backend espera
-Por tanto se debe hacer un DTO de envio
function mapUserToDTO(user: User): CreateUserDTO {
return {
nombre_completo: [Link],
fecha_creacion: [Link]()
};
}
await fetch("/api/users", {
method: "POST",
body: [Link](mapUserToDTO(user))
});
-No es necesario usarlo si el frontend y backend usan exactamente el
mismo formato y el proyecto es pequeño
-Muchas APIs tienen: UserResponseDTO, CreateUserDTO, UpdateUserDTO
-Muchas veces se usa DTO = Mapper, entonces simplemente cuando alguien
hable de DTO, es mejor pensar en un mapper
ESTRUCTURA RECOMENDADA CUANDO SE TIENE DTO
src/
app/
routes/
providers/
store/ # si usas redux/zustand
shared/
api/
[Link] # fetch wrapper / axios instance
[Link]
types/
[Link] # ID, ISODateString, etc
utils/
features/
auth/
api/
[Link]
[Link] # LoginRequestDTO, LoginResponseDTO
[Link] # mapLoginResponseDTOToAuthSession
model/
[Link] # AuthSession, AuthState (interno)
components/
[Link]
hooks/
[Link]
users/
api/
[Link]
[Link] # UserDTO, CreateUserDTO, UpdateUserDTO
[Link] # mapUserDTOToUser, mapUserToCreateUserDTO
model/
[Link] # User (interno), UserStatus, etc
components/
[Link]
pages/
[Link]
TEMA 3 - OPTIONAL CHAINING
-Es un operador que nos permite acceder de forma segura a una propiedad o
funcion de un objeto
-Si intentamos acceder a una propiedad que no existe ocurrira error en
runtime: Cannot read property 'avatar' of undefined
-[Link]?.avatar // Si profile existe dame avatar, si no, devuelve
undefined sin romper la app
CASOS DE USO
return (
<div>
<img src={[Link]?.avatar} />
</div>
)
-Sin optional chaining si se romperia
-[Link]?.avatar?.length // Si alguno no existe dame undefined
-Ocurre el error cuando intentamos acceder a una propiedad de una
propiedad que no existe, no cuando simplemente acceder a esa propiedad
que no existe, en ese caso nos arrojara undefined
MUY IMPORTANTE
-Optional chaining no evita errores de tipo si TypeScript sabe que algo
puede ser null o undefined
VIEJA FORMA
-Se usaba [Link] && [Link]
-Es menos claro y no funciona igual con valroes falsy
TEMA 4 - NULLISH COALESCING
-Es un operador para poner un valor por defecto real
-Este operador casi siempre se usa junto con ?.
EJM:
<p>{user?.profile?.bio ?? "Sin biografía"}</p>
<img src={user?.profile?.avatar ?? "/[Link]"} />
FASE 5
TEMA 1 - GENERICS
-Un Generic es un tipo dinamico que se define al momento de usar la
funcion
-Resuelve el problema de construir una funcion igual pero que solo cambie
el tipo de dato
-Lo que causa repeticion y poca escalabilidad
function identity<T> (valor: T): T{
return valor;
}
indentity<string>("hola")
indentity<number>(1)
EN REACT
-UseState es un Generic, internamente esta escrito asi:
function useState<T>(initialValue : T) : [T, function]{}
CUANDO USAR?
-Cuando queremos que algo funcione con multiples tipos
-No queremos usar any
async function fetchData<T>(url: string): Promise<T> {
const response = await fetch(url)
return [Link]()
}
type User = {
id: number
name: string
}
const user = await fetchData<User>("/api/user")
TEMA 2 - UTILITY TYPES
-Los utility Types son tipos genericos ya construidos por TypeScript para
transformar otros tipos
-Los mas importantes son Partial, Pick, Omit
PROBLEMA:
type User = {
id: number
name: string
email: string
password: string
}
-Supongamos que cuando creamos un usuario no se tiene id, o cuando se
actuliza un usuario, no siempre envias todos los campos, o que no
queremos enviar password al frontend
-Crear un tipo para cada una de estas situaciones seria repetitivo y poco
mantenible
PARTIAL<T>
-Convierte todas las propiedades a opcionales
-Sin partial:
type UpdateUser = {
id?: number
name?: string
}
-Con partial:
type UpdateUser = Partial<User>
function updateUser(data: Partial<User>){} // Cuando hacemos un PATCH
PICK<T, K>
-Permite elegir solo ciertas propiedades
-Un caso real es para listar usuarios en una tabla
type UserListItem= Pick<User, "id" | "name", "email">
type UserPreview = {
id: number
name: string
email: string
}
OMIT<T, K>
-Hace lo contrario de Pick, elimina propiedades
-Un caso real es cuando se envia datos al frontend
function getUser(): Omit<User, "password">{
}
-Muy comun en DTOs frontend con Partial para updates parciales (PATCH)
type UpdateUserDTO = Partial<Omit<User, 'id'>>;