├── .gitignore ├── typescript-utility-types ├── README.md ├── 10-non-nullable.ts ├── 11-awaited.ts ├── package.json ├── 09-parameters.ts ├── 05-readonly.ts ├── 04-pick.ts ├── 08-return-type.ts ├── 02-required.ts ├── 07-extract.ts ├── 06-exclude.ts ├── 03-omit.ts ├── package-lock.json ├── 01-partial.ts ├── tsconfig.json └── main.ts └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .env -------------------------------------------------------------------------------- /typescript-utility-types/README.md: -------------------------------------------------------------------------------- 1 | Contenido de mi curso en https://midu.dev -------------------------------------------------------------------------------- /typescript-utility-types/10-non-nullable.ts: -------------------------------------------------------------------------------- 1 | // --- 10: NonNullable --- 2 | // Crea un tipo excluyendo null y undefined de T. -------------------------------------------------------------------------------- /typescript-utility-types/11-awaited.ts: -------------------------------------------------------------------------------- 1 | // --- 11: Awaited --- 2 | // Desenvuelve el tipo de una Promesa, obteniendo el tipo que resuelve. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Contenido de los cursos de https://midu.dev 2 | 3 | ![CleanShot 2025-09-18 at 11 33 01@2x](https://github.com/user-attachments/assets/129c4224-c287-495a-a262-ad43865e7214) 4 | -------------------------------------------------------------------------------- /typescript-utility-types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-utility-types", 3 | "version": "1.0.0", 4 | "description": "Contenido de mi curso en https://midu.dev", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "typescript": "^5.9.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /typescript-utility-types/09-parameters.ts: -------------------------------------------------------------------------------- 1 | // --- 09: Parameters --- 2 | // Obtiene los tipos de los parámetros de una función como una tupla. 3 | 4 | function sendMessage(chatId: string, message: string, attachment?: File) { 5 | // Lógica para enviar mensaje... 6 | } 7 | 8 | type SendMessageParams = Parameters; 9 | 10 | const sendMessageParams: SendMessageParams = [ 11 | "#general", 12 | "Hola a todos" 13 | ] 14 | 15 | sendMessage(...sendMessageParams) 16 | 17 | -------------------------------------------------------------------------------- /typescript-utility-types/05-readonly.ts: -------------------------------------------------------------------------------- 1 | // --- 05: Readonly --- 2 | // Hace que todas las propiedades de un tipo sean de solo lectura. 3 | 4 | interface Pizza { 5 | name: string; 6 | toppings: Readonly; 7 | } 8 | 9 | const pizza: Readonly = { 10 | name: 'Margherita', 11 | toppings: ['Tomato', 'Mozzarella', 'Basil'] 12 | } 13 | 14 | // no se pueden modificar las propiedades 15 | pizza.name = 'Pepperoni'; // Error 16 | pizza.toppings.push('Pepperoni'); // Error 17 | 18 | -------------------------------------------------------------------------------- /typescript-utility-types/04-pick.ts: -------------------------------------------------------------------------------- 1 | // --- 04: Pick --- 2 | // Crea un tipo seleccionando solo las propiedades K del tipo T. 3 | 4 | interface Movie { 5 | title: string 6 | director: string 7 | year: number 8 | genre: string 9 | rating: number 10 | } 11 | 12 | type MovieBasicInfo = Pick 13 | 14 | const movieBasicInfo: MovieBasicInfo = { title: 'Inception', director: 'Christopher Nolan' } 15 | 16 | type MovieRating = Pick 17 | 18 | const movieRating: MovieRating = { rating: 8 } 19 | 20 | const rating: Movie["rating"] = 8 -------------------------------------------------------------------------------- /typescript-utility-types/08-return-type.ts: -------------------------------------------------------------------------------- 1 | // --- 08: ReturnType --- 2 | // Obtiene el tipo de retorno de una función 3 | 4 | const createAuthResponse = (success: boolean) => { 5 | if (success) { 6 | const token = crypto.randomUUID() 7 | return { status: 200, token } as const 8 | } 9 | 10 | return { status: 401, error: "Unauthorized" } as const 11 | } 12 | 13 | const response = createAuthResponse(true) 14 | 15 | type AuthResponse = ReturnType 16 | 17 | const authResponse: AuthResponse = { 18 | status: 401, 19 | error: "Unauthorized" 20 | } -------------------------------------------------------------------------------- /typescript-utility-types/02-required.ts: -------------------------------------------------------------------------------- 1 | // --- 02: Required --- 2 | // Hace que todas las propiedades de un tipo (incluso las opcionales) sean requeridas. 3 | 4 | interface ProductIncomplete { 5 | id: number 6 | name?: string 7 | price?: number 8 | } 9 | 10 | type ProductComplete = Required 11 | 12 | const product: ProductIncomplete = { id: 1 } 13 | 14 | const productComplete: ProductComplete = { 15 | id: 1, 16 | name: 'Switch 2', 17 | price: 300, 18 | } 19 | 20 | const productCompleteWithError: ProductComplete = { 21 | id: 1, 22 | // ❌ Error: Missing properties 'name' and 'price' 23 | } -------------------------------------------------------------------------------- /typescript-utility-types/07-extract.ts: -------------------------------------------------------------------------------- 1 | // --- 07: Extract --- 2 | // Crea un tipo extrayendo de T los tipos que son asignables a U. Es lo opuesto a Exclude. 3 | 4 | type Pokemon = 5 | | { kind: "pikachu"; thunderLevel: number } 6 | | { kind: "bulbasaur"; venomLevel: number } 7 | | { kind: "magikarp"; waterLevel: number } 8 | | { kind: "charmander"; fireLevel: number } 9 | | { kind: "squirtle"; waterLevel: number } 10 | | { kind: "psyduck"; waterLevel: number } 11 | | { kind: "vaporeon"; waterLevel: number } 12 | 13 | type WaterPokemon = Extract 14 | 15 | const waterPokemon: WaterPokemon = { kind: "squirtle", waterLevel: 100 } 16 | -------------------------------------------------------------------------------- /typescript-utility-types/06-exclude.ts: -------------------------------------------------------------------------------- 1 | // --- 06: Exclude --- 2 | // Excluye de un tipo unión los elementos que indiques 3 | 4 | // Ejemplo 01: 5 | type Characters = "Iron Man" | "Thor" | "Loki" | "Hulk" | "Thanos" 6 | 7 | type Avengers = Exclude 8 | type Villains = Exclude 9 | 10 | const hero: Avengers = "Thor" 11 | const villain: Villains = "Thanos" 12 | 13 | // Ejemplo 02: 14 | type Animal = 15 | | { kind: "dog"; bark: string } 16 | | { kind: "cat"; meow: string } 17 | | { kind: "fish"; swimSpeed: number } 18 | 19 | type NoCatAllowed = Exclude 20 | 21 | const animal: NoCatAllowed = { 22 | kind: "dog", // Error 23 | bark: 'woooofff' 24 | } -------------------------------------------------------------------------------- /typescript-utility-types/03-omit.ts: -------------------------------------------------------------------------------- 1 | // --- 03: Omit --- 2 | // Crea un tipo eliminando las propiedades K del tipo T. 3 | 4 | interface Avenger { 5 | name: string 6 | power: number 7 | weapon?: string 8 | alive: boolean 9 | } 10 | 11 | type AvengerWithoutSpoilers = Omit 12 | 13 | const ironMan: AvengerWithoutSpoilers = { 14 | name: 'Iron Man', 15 | power: 9000, 16 | weapon: 'Un millionario con armadura' 17 | } 18 | 19 | type AvengerBasicInfo = Omit 20 | 21 | const ironManBasic: AvengerBasicInfo = { 22 | name: 'Iron Man', 23 | power: 9000 24 | } 25 | 26 | const ironManBasicError: AvengerBasicInfo = { 27 | name: 'Iron Man', 28 | power: 9000, 29 | weapon: 'Un millionario con armadura' // Error: La propiedad 'weapon' no existe en el tipo 'AvengerBasicInfo' 30 | } -------------------------------------------------------------------------------- /typescript-utility-types/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-utility-types", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "typescript-utility-types", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "typescript": "^5.9.2" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "5.9.2", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", 18 | "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", 19 | "dev": true, 20 | "license": "Apache-2.0", 21 | "bin": { 22 | "tsc": "bin/tsc", 23 | "tsserver": "bin/tsserver" 24 | }, 25 | "engines": { 26 | "node": ">=14.17" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /typescript-utility-types/01-partial.ts: -------------------------------------------------------------------------------- 1 | // --- 01: Partial --- 2 | // Hace que todas las propiedades de un tipo sean opcionales. 3 | 4 | console.log("--- 01: Partial ---"); 5 | 6 | // Ejemplo 1: Actualizar un Perfil de Usuario 7 | interface User { 8 | id: number; 9 | name: string; 10 | email: string; 11 | isAdmin: boolean; 12 | } 13 | 14 | function updateUser(id: number, fieldsToUpdate: Partial) { 15 | console.log(`Actualizando usuario ${id} con:`, fieldsToUpdate); 16 | // Lógica para encontrar y actualizar el usuario en la BD... 17 | } 18 | 19 | updateUser(1, { email: "nuevo@correo.com" }); // ✅ 20 | updateUser(2, { name: "Ana", isAdmin: false }); // ✅ 21 | updateUser(3, { age: 18 }); // Error: 'age' no existe en 'User' 22 | 23 | // Ejemplo 2: Filtros de Búsqueda Opcionales 24 | interface ProductFilters { 25 | category: string; 26 | minPrice: number; 27 | maxPrice: number; 28 | } 29 | 30 | function findProducts(filters: Partial) { 31 | console.log("Buscando con los filtros:", filters); 32 | // Lógica para buscar productos aplicando solo los filtros presentes... 33 | } 34 | 35 | findProducts({ category: "electrónica" }); // ✅ 36 | findProducts({ minPrice: 100, maxPrice: 500 }); // ✅ 37 | findProducts({ color: "rojo" }); // Error: 'color' no existe en 'ProductFilters' -------------------------------------------------------------------------------- /typescript-utility-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // Visit https://aka.ms/tsconfig to read more about this file 3 | "compilerOptions": { 4 | // File Layout 5 | // "rootDir": "./src", 6 | // "outDir": "./dist", 7 | 8 | // Environment Settings 9 | // See also https://aka.ms/tsconfig/module 10 | "module": "nodenext", 11 | "target": "esnext", 12 | "types": [], 13 | // For nodejs: 14 | // "lib": ["esnext"], 15 | // "types": ["node"], 16 | // and npm install -D @types/node 17 | 18 | // Other Outputs 19 | "sourceMap": true, 20 | "declaration": true, 21 | "declarationMap": true, 22 | 23 | // Stricter Typechecking Options 24 | "noUncheckedIndexedAccess": true, 25 | "exactOptionalPropertyTypes": true, 26 | 27 | // Style Options 28 | // "noImplicitReturns": true, 29 | // "noImplicitOverride": true, 30 | // "noUnusedLocals": true, 31 | // "noUnusedParameters": true, 32 | // "noFallthroughCasesInSwitch": true, 33 | // "noPropertyAccessFromIndexSignature": true, 34 | 35 | // Recommended Options 36 | "strict": true, 37 | "jsx": "react-jsx", 38 | "verbatimModuleSyntax": true, 39 | "isolatedModules": true, 40 | "noUncheckedSideEffectImports": true, 41 | "moduleDetection": "force", 42 | "skipLibCheck": true, 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /typescript-utility-types/main.ts: -------------------------------------------------------------------------------- 1 | // ======================================================= 2 | // CURSO DE UTILITY TYPES DE TYPESCRIPT - EJEMPLOS 3 | // ======================================================= 4 | 5 | 6 | // --- 01: Partial --- 7 | // Hace que todas las propiedades de un tipo sean opcionales. 8 | 9 | console.log("--- 01: Partial ---"); 10 | 11 | // Ejemplo 1: Actualizar un Perfil de Usuario 12 | interface User { 13 | id: number; 14 | name: string; 15 | email: string; 16 | isAdmin: boolean; 17 | } 18 | 19 | function updateUser(id: number, fieldsToUpdate: Partial) { 20 | console.log(`Actualizando usuario ${id} con:`, fieldsToUpdate); 21 | // Lógica para encontrar y actualizar el usuario en la BD... 22 | } 23 | 24 | updateUser(1, { email: "nuevo@correo.com" }); 25 | updateUser(2, { name: "Ana", isAdmin: false }); 26 | 27 | // Ejemplo 2: Filtros de Búsqueda Opcionales 28 | interface ProductFilters { 29 | category: string; 30 | minPrice: number; 31 | maxPrice: number; 32 | } 33 | 34 | function findProducts(filters: Partial) { 35 | console.log("Buscando con los filtros:", filters); 36 | // Lógica para buscar productos aplicando solo los filtros presentes... 37 | } 38 | 39 | findProducts({ category: "electrónica" }); 40 | findProducts({ minPrice: 100, maxPrice: 500 }); 41 | 42 | 43 | // --- 02: Required --- 44 | // Hace que todas las propiedades de un tipo (incluso las opcionales) sean requeridas. 45 | 46 | console.log("\n--- 02: Required ---"); 47 | 48 | // Ejemplo 1: Configuración de una Aplicación 49 | interface AppConfig { 50 | port?: number; 51 | databaseUrl?: string; 52 | environment: "development" | "production"; 53 | } 54 | 55 | function finalizeConfig(config: AppConfig): Required { 56 | return { 57 | port: config.port ?? 3000, 58 | databaseUrl: config.databaseUrl ?? "mongodb://localhost/db", 59 | environment: config.environment, 60 | }; 61 | } 62 | 63 | const finalConfig = finalizeConfig({ environment: "production" }); 64 | console.log("Configuración final:", finalConfig); 65 | 66 | 67 | // --- 03: Omit --- 68 | // Crea un tipo eliminando las propiedades K del tipo T. 69 | 70 | console.log("\n--- 03: Omit ---"); 71 | 72 | // Ejemplo 1: Eliminar Datos Sensibles 73 | interface UserWithPassword { 74 | id: number; 75 | name: string; 76 | email: string; 77 | passwordHash: string; 78 | } 79 | 80 | type PublicUser = Omit; 81 | 82 | const publicProfile: PublicUser = { 83 | id: 1, 84 | name: "Carlos", 85 | email: "carlos@correo.com", 86 | }; 87 | console.log("Perfil público del usuario:", publicProfile); 88 | 89 | // Ejemplo 2: DTO para Crear un Recurso 90 | interface Post { 91 | id: string; 92 | title: string; 93 | content: string; 94 | createdAt: Date; 95 | } 96 | 97 | type CreatePostDTO = Omit; 98 | 99 | function createNewPost(data: CreatePostDTO) { 100 | const newPost: Post = { 101 | id: crypto.randomUUID(), 102 | ...data, 103 | createdAt: new Date(), 104 | }; 105 | console.log("Creando nuevo post:", newPost); 106 | } 107 | 108 | createNewPost({ title: "Mi primer Post", content: "¡Hola Mundo!" }); 109 | 110 | 111 | // --- 04: Pick --- 112 | // Crea un tipo seleccionando solo las propiedades K del tipo T. 113 | 114 | console.log("\n--- 04: Pick ---"); 115 | 116 | // Ejemplo 1: Crear una Tarjeta de Vista Previa 117 | interface BlogPost { 118 | id: string; 119 | title: string; 120 | content: string; 121 | author: string; 122 | views: number; 123 | tags: string[]; 124 | } 125 | 126 | type PostPreview = Pick; 127 | 128 | const previewData: PostPreview = { 129 | title: "Aprende Utility Types", 130 | author: "Gemini", 131 | tags: ["typescript", "desarrollo-web"], 132 | }; 133 | console.log("Vista previa del post:", previewData); 134 | 135 | 136 | // --- 05: ReadOnly --- 137 | // Hace que todas las propiedades de un tipo sean de solo lectura. 138 | 139 | console.log("\n--- 05: ReadOnly ---"); 140 | 141 | // Ejemplo 1: Constantes de configuración 142 | interface AppSettings { 143 | readonly apiUrl: string; 144 | theme: "light" | "dark"; 145 | } 146 | 147 | const settings: Readonly = { 148 | apiUrl: "/api/v1", 149 | theme: "dark", 150 | }; 151 | 152 | // settings.apiUrl = "/api/v2"; // Error: Cannot assign to 'apiUrl' because it is a read-only property. 153 | console.log("Configuración de la app:", settings); 154 | 155 | 156 | // --- 06: Exclude --- 157 | // Crea un tipo excluyendo de T los tipos que son asignables a U. Útil con uniones. 158 | 159 | console.log("\n--- 06: Exclude ---"); 160 | 161 | // Ejemplo 1: Estados de una Petición 162 | type RequestStatus = "loading" | "success" | "error"; 163 | type FinalStatus = Exclude; 164 | 165 | const status: FinalStatus = "success"; 166 | // const anotherStatus: FinalStatus = "loading"; // Error: Type '"loading"' is not assignable to type 'FinalStatus'. 167 | console.log("Estado final de la petición:", status); 168 | 169 | 170 | // --- 07: Extract --- 171 | // Crea un tipo extrayendo de T los tipos que son asignables a U. Es lo opuesto a Exclude. 172 | 173 | console.log("\n--- 07: Extract ---"); 174 | 175 | // Ejemplo 1: Filtrar eventos 176 | type AppEvent = "click" | "hover" | "submit" | "keydown"; 177 | type MouseEvent = Extract; 178 | 179 | const mouseAction: MouseEvent = "click"; 180 | console.log("Evento del ratón:", mouseAction); 181 | 182 | 183 | // --- 08: ReturnType --- 184 | // Obtiene el tipo de retorno de una función. 185 | 186 | console.log("\n--- 08: ReturnType ---"); 187 | 188 | function createAuthResponse(success: boolean) { 189 | if (success) { 190 | return { status: 200, token: "abc-123" }; 191 | } 192 | return { status: 401, error: "Unauthorized" }; 193 | } 194 | 195 | type AuthResponse = ReturnType; 196 | 197 | const successResponse: AuthResponse = { status: 200, token: "xyz-456" }; 198 | console.log("Respuesta de autenticación:", successResponse); 199 | 200 | 201 | // --- 09: Parameters --- 202 | // Obtiene los tipos de los parámetros de una función como una tupla. 203 | 204 | console.log("\n--- 09: Parameters ---"); 205 | 206 | function sendMessage(chatId: string, message: string, attachment?: File) { 207 | // Lógica para enviar mensaje... 208 | } 209 | 210 | type MessageParams = Parameters; 211 | 212 | const file = new File(["contenido"], "archivo.txt", { type: "text/plain" }); 213 | const messageArgs: MessageParams = ["#general", "Hola equipo!", file]; 214 | console.log("Argumentos de la función sendMessage:", messageArgs); 215 | 216 | 217 | // --- 10: NonNullable --- 218 | // Crea un tipo excluyendo null y undefined de T. 219 | 220 | console.log("\n--- 10: NonNullable ---"); 221 | 222 | type MaybeString = string | null | undefined; 223 | 224 | function processText(text: NonNullable) { 225 | console.log(text.trim()); 226 | } 227 | 228 | let textInput: MaybeString = " Hola Mundo "; 229 | if (textInput) { 230 | processText(textInput); // Dentro del if, TypeScript sabe que no es null/undefined 231 | } 232 | 233 | 234 | // --- 11: Awaited --- 235 | // Desenvuelve el tipo de una Promesa, obteniendo el tipo que resuelve. 236 | 237 | console.log("\n--- 11: Awaited ---"); 238 | 239 | async function fetchUserData(id: number): Promise { 240 | // Simula una llamada a una API 241 | return { id, name: "Elena", email: "elena@correo.com", isAdmin: true }; 242 | } 243 | 244 | type FetchedUser = Awaited>; 245 | 246 | async function displayUser() { 247 | const user: FetchedUser = await fetchUserData(10); 248 | console.log(`Usuario obtenido con Awaited: ${user.name}`); 249 | } 250 | 251 | displayUser(); --------------------------------------------------------------------------------