├── CONTRIBUYENDO.md ├── LICENSE ├── .all-contributorsrc ├── COLABORADORES.md ├── GUIA.md ├── MIGRANDO.md ├── HOC.md ├── IGNORATE.md └── AVANZADO.md /CONTRIBUYENDO.md: -------------------------------------------------------------------------------- 1 | # ¡Entonces quieres contribuir! 2 | 3 | ## ⚠️ IMPORTANTE 4 | 5 | Si desea contribuir un ejemplo a la [Cheatsheet Original](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet), abre un _Issue_ en el repositorio original que está en inglés. De ahi se puede agregar al repositorio traducido en español. 6 | 7 | --- 8 | 9 | ## Traducción 10 | 11 | **¿Quieres contribuir ayudando a traducir estos recursos?** 12 | 13 | **¡Increíble!** 14 | 15 | Primero, revise este _[issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/issues/2)_ que describe el progreso y lea la [Guía de Estilo](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/blob/master/GUIA.md) para traducir. 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 TypeScript Cheatsheets 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "COLABORADORES.md", 4 | "README.md" 5 | ], 6 | "contributors": [ 7 | { 8 | "login": "laurosilvacom", 9 | "name": "Lauro Silva", 10 | "avatar_url": "https://avatars2.githubusercontent.com/u/57044804?v=4", 11 | "profile": "https://laurosilva.com", 12 | "contributions": [ 13 | "maintenance", 14 | "code", 15 | "content" 16 | ] 17 | }, 18 | { 19 | "login": "aromanarguello", 20 | "name": "aromanarguello", 21 | "avatar_url": "https://avatars0.githubusercontent.com/u/28843542?v=4", 22 | "profile": "https://github.com/aromanarguello", 23 | "contributions": [ 24 | "code", 25 | "doc", 26 | "translation" 27 | ] 28 | }, 29 | { 30 | "login": "carburo", 31 | "name": "Rainer Martínez Fraga", 32 | "avatar_url": "https://avatars1.githubusercontent.com/u/4054941?v=4", 33 | "profile": "https://github.com/carburo", 34 | "contributions": [ 35 | "translation", 36 | "code", 37 | "doc" 38 | ] 39 | }, 40 | { 41 | "login": "hlebon", 42 | "name": "Hans Garcia", 43 | "avatar_url": "https://avatars1.githubusercontent.com/u/18727829?v=4", 44 | "profile": "https://hansgarcia.dev/", 45 | "contributions": [ 46 | "translation", 47 | "code", 48 | "doc" 49 | ] 50 | }, 51 | { 52 | "login": "dantehemerson", 53 | "name": "Dante Calderón", 54 | "avatar_url": "https://avatars3.githubusercontent.com/u/18385321?v=4", 55 | "profile": "https://dantecalderon.dev", 56 | "contributions": [ 57 | "translation", 58 | "code", 59 | "doc" 60 | ] 61 | } 62 | ], 63 | "imageSize": 100, 64 | "commit": true, 65 | "commitConvention": "angular", 66 | "contributorsPerLine": 7, 67 | "projectName": "react-typescript-cheatsheet-es", 68 | "projectOwner": "typescript-cheatsheets", 69 | "repoType": "github", 70 | "repoHost": "https://github.com", 71 | "skipCi": true 72 | } 73 | -------------------------------------------------------------------------------- /COLABORADORES.md: -------------------------------------------------------------------------------- 1 | ## Contributors 2 | 3 | Gracias a todas esas maravillosas personas ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Lauro Silva

🚧 💻 🖋

aromanarguello

💻 📖 🌍

Rainer Martínez Fraga

🌍 💻 📖

Hans Garcia

🌍 💻 📖

Dante Calderón

🌍 💻 📖
17 | 18 | 19 | 20 | 21 | 22 | 23 | Este proyecto sigue la specificación de [all-contributors](https://allcontributors.org/docs/es-es/overview). ¡Todo tipo de colaboradores son bienvenidos! 24 | -------------------------------------------------------------------------------- /GUIA.md: -------------------------------------------------------------------------------- 1 | # Guía de estilo 2 | 3 | ## Texto en bloques de código 4 | 5 | Deja el texto en los bloques de código sin traducir, excepto para los comentarios. Opcionalmente puedes traducir el texto en cadenas, ¡pero cuida de no traducir cadenas que hagan referencia a código! 6 | 7 | Ejemplo: 8 | 9 | ```js 10 | // Example 11 | const element =

Hello, world

; 12 | ReactDOM.render(element, document.getElementById("root")); 13 | ``` 14 | 15 | ✅ CORRECTO: 16 | 17 | ```js 18 | // Ejemplo 19 | const element =

Hello, world

; 20 | ReactDOM.render(element, document.getElementById("root")); 21 | ``` 22 | 23 | ✅ TAMBIÉN CORRECTO: 24 | 25 | ```js 26 | // Ejemplo 27 | const element =

Hola mundo

; 28 | ReactDOM.render(element, document.getElementById("root")); 29 | ``` 30 | 31 | ❌ INCORRECTO: 32 | 33 | ```js 34 | // Ejemplo 35 | const element =

Hola mundo

; 36 | // "root" hace referencia a un indentificador de un elemento. 37 | // NO LO TRADUZCAS 38 | ReactDOM.render(element, document.getElementById("raíz")); 39 | ``` 40 | 41 | ❌ DEFINITIVAMENTE INCORRECTO: 42 | 43 | ```js 44 | // Ejemplo 45 | const elemento =

Hola mundo

; 46 | ReactDOM.hacer(elemento, documento.obtenerElementoPorId("raíz")); 47 | ``` 48 | 49 | ## Enlaces externos 50 | 51 | Si un enlace externo es a un artículo en un sitio de referencias como [MDN] o [Wikipedia] y existe una versión de este artículo en español con una calidad aceptable, considera sustituir el enlace por el de esa versión. 52 | 53 | [mdn]: https://developer.mozilla.org/en-US/ 54 | [wikipedia]: https://en.wikipedia.org/wiki/Main_Page 55 | 56 | Ejemplo: 57 | 58 | ```md 59 | React elements are [immutable](https://en.wikipedia.org/wiki/Immutable_object). 60 | ``` 61 | 62 | ✅ BIEN: 63 | 64 | ```md 65 | Los elementos de React son [inmutables](https://es.wikipedia.org/wiki/Objeto_inmutable). 66 | ``` 67 | 68 | Para enlaces que no tienen un equivalente en español (Stack Overflow, videos de YouTube, etcétera) mantén el enlace en inglés. 69 | 70 | ## Usted, tú y vos 71 | 72 | Para mantener la consistencia y evitar regionalismos decidimos utilizar tú para la segunda persona del singular. 73 | 74 | ## Traducciones comunes 75 | 76 | Aquí hay algunas sugerencias para la traducción de términos de uso común en este tipo de documentación técnica. 77 | 78 | | Original word/term | Suggestion | 79 | | ---------------------- | ------------------------------------- | 80 | | array | _array_ | 81 | | arrow function | función flecha | 82 | | assert | comprobar | 83 | | bug | error | 84 | | bundler | _bundler_ | 85 | | callback | _callback_ | 86 | | camelCase | _camelCase_ | 87 | | controlled component | componente controlado | 88 | | Children | _Children_ | 89 | | Cheatsheet | _Cheatsheet_ | 90 | | debugging | depuración | 91 | | DOM | DOM | 92 | | framework | _framework_ | 93 | | function component | componente de función | 94 | | hook | _hook_ | 95 | | key | _key_ | 96 | | lazy initialization | inicialización diferida | 97 | | library | biblioteca | 98 | | linting | _linting_ | 99 | | lowercase | minúscula(s) | 100 | | props | _props_ | 101 | | props drilling | _props drilling_ | 102 | | React element | Elemento de React | 103 | | Type | _tipo_ | 104 | | Types | _tipos_ | 105 | | Namespaced | _Namespaced_ | 106 | | render | renderizar (verb), renderizado (noun) | 107 | | shallow rendering | renderizado superficial | 108 | | state | estado | 109 | | string | _string_ | 110 | | template literals | _template literals_ | 111 | | uncontrolled component | componente no controlado | 112 | 113 | Si desea agregar algo que falta, abra un [issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/issues/new). 114 | -------------------------------------------------------------------------------- /MIGRANDO.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | react + ts logo 11 | 12 | 13 |

Cheatsheets para desarrolladores expertos en React que comienzan con TypeScript

14 | 15 | [**Básico**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es#tabla-de-contenidos-de-la-cheatsheet-básica) | 16 | [**Avanzado**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/blob/master/AVANZADO.md) | 17 | [**Migrando**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/blob/master/MIGRANDO.md) | 18 | [**HOC**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/blob/master/HOC.md) | 19 | [**Inglés**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet) | 20 | [**中文翻译**](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) | 21 | [Contribuir](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/blob/master/CONTRIBUYENDO.md) | 22 | [Preguntas](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/issues/new) 23 | 24 |
25 | 26 | --- 27 | 28 | # Migrando (a TypeScript) Cheatsheet 29 | 30 | Este Cheatsheet recopila consejos y utilidades de casos de estudios reales de equipos que mueven bases de código significativas de JS plano o Flow a Typescript. Esto no intenta _convencer_ a la gente para que lo haga, pero recopilamos las pocas estadísticas que ofrecen las empresas después de su experiencia de migración. 31 | 32 | > ⚠️ Este Cheatsheet es extremadamente nuevo y podría usar toda la ayuda que podamos obtener. Consejos sólidos, resultados, y contenido actualizado son bienvenidos. 33 | 34 | ## Prerrequisitos 35 | 36 | Leer la [Guía oficial de TypeScript para migrar desde JS](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html) y también deberías estar familiarizado con la [Guía de conversión de React](https://github.com/Microsoft/TypeScript-React-Conversion-Guide#typescript-react-conversion-guide). 37 | 38 | ## Enfoques de conversión general 39 | 40 | - Nivel 0: No uses TypeScript, usa JSDoc 41 | - Mira nuestra [seccion de JSDoc](#JSDoc) 42 | - Nivel 1A: JavaScript mayoritario, TypeScript cada vez más estricto 43 | - como recomienda la [guía oficial de TS](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html) 44 | - usa `allowJS` (Experiencias: [clayallsop][clayallsop], [pleo][pleo]) 45 | - Nivel 1B: Renombrar todo a TypeScript desde el principio 46 | - "[Just rename all .js files to .ts](https://twitter.com/jamonholmgren/status/1089241726303199232)"? 47 | - usa lo mas simple, la configuración mas mínima para comenzar 48 | - Nivel 2: TypeScript estricto 49 | - usa [`dts-gen`](https://github.com/Microsoft/dts-gen) de Microsoft para generar archivos `.d.ts` para tus archivos sin tipo. [Esta respuesta de SO](https://stackoverflow.com/questions/12687779/how-do-you-produce-a-d-ts-typings-definition-file-from-an-existing-javascript) tiene mas información sobre el tema. 50 | - usa la palabra clasve `declare` para declaraciones de ambiente - mira la [declaración de fusión](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#troubleshooting-handbook-bugs-in-official-typings) para parchear declaraciones de biblioteca en línea. 51 | 52 | Varios consejos/enfoques que las empresas exitosas han tomado 53 | 54 | - `@ts-ignore` en errores del compilador para librerias sin typedefs. 55 | - elije ESLint sobre TSLint (fuente: [ESLint](https://eslint.org/blog/2019/01/future-typescript-eslint) y [TS Roadmap](https://github.com/Microsoft/TypeScript/issues/29288)). [Puedes convertir TSlint a ESlint con esta herramienta](https://github.com/typescript-eslint/tslint-to-eslint-config). 56 | - El nuevo código siempre de escribirse en TypeScript. Sin excepciones. Para el código existente: Si tu tarea requiere cambiar el código JavaScript, debes volverlo a escribir. (Source: [Hootsuite][hootsuite]) 57 | 58 |
59 | 60 | 61 | Consejos para Webpack 62 | 63 | 64 | 65 | - webpack loader: `awesome-typescript-loader` vs `ts-loader`? (Hay un cierto desacuerdo en la comunidad sobre esto - pero lee [awesome's point of view](https://github.com/s-panferov/awesome-typescript-loader#differences-between-ts-loader)) 66 | - configuración de Webpack: 67 | 68 | ```js 69 | module.exports = { 70 | 71 | resolve: { 72 | - extensions: ['.js', '.jsx'] 73 | + extensions: ['.ts', '.tsx', '.js', '.jsx'] 74 | }, 75 | 76 | // Soporte para source maps ('inline-source-map' también funciona) 77 | devtool: 'source-map', 78 | 79 | // Añadir el loader para archivos .ts. 80 | module: { 81 | loaders: [{ 82 | - test: /\.jsx?$/, 83 | - loader: 'babel-loader', 84 | - exclude: [/node_modules/], 85 | + test: /\.(t|j)sx?$/, 86 | + loader: ['awesome-typescript-loader?module=es6'], 87 | + exclude: [/node_modules/] 88 | + }, { 89 | + test: /\.js$/, 90 | + loader: 'source-map-loader', 91 | + enforce: 'pre' 92 | }] 93 | } 94 | }; 95 | ``` 96 | 97 | Nota especial sobre `ts-loader` y librerias de terceros: https://twitter.com/acemarke/status/1091150384184229888 98 | 99 |
100 | 101 | ## JSDoc 102 | 103 | - https://github.com/Microsoft/TypeScript/wiki/JsDoc-support-in-JavaScript 104 | - El código base de webpack usa JSDoc con linting de TS https://twitter.com/TheLarkInn/status/984479953927327744 (un truco loco: https://twitter.com/thelarkinn/status/996475530944823296) 105 | 106 | Problemas a tener en cuenta: 107 | 108 | - `object` se convierte a `any` por alguna razón. 109 | - Si tiene un error en el JSDoc, no recibes ningún warning/error. TS just silently doesn't type annotate the function. 110 | - [El casteo puede ser detallado](https://twitter.com/bahmutov/status/1089229349637754880) 111 | 112 | (_gracias [Gil Tayar](https://twitter.com/giltayar/status/1089228919260221441) y [Gleb Bahmutov](https://twitter.com/bahmutov/status/1089229196247908353) por compartir el comentario anterior_) 113 | 114 | ## Desde JS 115 | 116 | ### Conversión automatizada de JS a TS 117 | 118 | - [TypeStat](https://github.com/JoshuaKGoldberg/TypeStat) ([usado por Codecademy](https://mobile.twitter.com/JoshuaKGoldberg/status/1159090281314160640)) 119 | - [TypeWiz](https://github.com/urish/typewiz) 120 | - [js-to-ts-converter](https://github.com/gregjacobs/js-to-ts-converter) 121 | 122 | ### Conversión manual de JS a TS 123 | 124 | la estrategia de "Solo renombra" 125 | 126 | - OSX/Linux: `find src -name "*.js" -exec sh -c 'mv"$0" "${0%.js}.tsx"' {} \;` 127 | 128 | Puede cargar archivos de TypeScript con webpack o usar el compilador `tsc` para compilar sus archivos TS en JS uno al lado del otro. El `tsconfig.json` básico es: 129 | 130 | ```json 131 | { 132 | "compilerOptions": { 133 | "allowJs": true 134 | } 135 | } 136 | ``` 137 | 138 | Luego querrás habilitarlo para validar JS: 139 | 140 | ```json 141 | { 142 | "compilerOptions": { 143 | "allowJs": true, 144 | "checkJs": true 145 | } 146 | } 147 | ``` 148 | 149 | Si tiene una base de código grande y arroja demasiados errores a la vez, puedes optar por excluir archivos problemáticos con `//@ts-nocheck` o en su lugar desactivar `checkJs` y agregar una directiva `// @ ts-check` en la parte superior de cada archivo JS normal. 150 | 151 | TypeScript debería arrojar algunos errores atroces aquí que deberían ser fáciles de solucionar. 152 | 153 | Una vez que haya terminado, trague la píldora roja apagando los `any` implícitos: 154 | 155 | ```js 156 | { 157 | "compilerOptions": { 158 | "allowJs": true, 159 | "checkJs": true, 160 | "noImplicitAny": true // o "strict": true 161 | } 162 | } 163 | ``` 164 | 165 | Esto generará un montón de errores de tipo y puedes comenzar a convertir archivos a TS u (opcionalmente) usar [anotaciones JSDoc](https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html) en tu JS. 166 | 167 | Una práctica común es usar alias de tipo TODO para `any`, para que puedas hacer un seguimiento de lo que necesita hacer luego. 168 | 169 | ```ts 170 | type TODO_TYPEME = any; 171 | export function myFunc(foo: TODO_TYPEME, bar: TODO_TYPEME): number { 172 | // ... 173 | } 174 | ``` 175 | 176 | Gradualmente agrega [mas banderas de modo `strict`](https://www.typescriptlang.org/docs/handbook/compiler-options.html) como `noImplicitThis`, `strictNullChecks`, y así sucesivamente hasta que finalmente pueda correr por completo en modo estricto sin que queden archivos js: 177 | 178 | ```js 179 | { 180 | "compilerOptions": { 181 | "strict": true 182 | } 183 | } 184 | ``` 185 | 186 | **Más recursos** 187 | 188 | - [Adopting TypeScript at Scale - AirBnB's conversion story and strategy](https://www.youtube.com/watch?v=P-J9Eg7hJwE) 189 | - [Migrating a `create-react-app`/`react-scripts` app to TypeScript](https://facebook.github.io/create-react-app/docs/adding-typescript) - don't use `react-scripts-ts` 190 | - [Migrating an EJECTED CRA app to TS](https://spin.atomicobject.com/2018/07/04/migrating-cra-typescript/) 191 | - [Lyft's JS to TS migration tool](https://github.com/lyft/react-javascript-to-typescript-transform) (includes PropTypes migration) 192 | - [Hootsuite][hootsuite] 193 | - [Storybook's migration (PR)](https://github.com/storybooks/storybook/issues/5030) 194 | - [How we migrated a 200K+ LOC project to TypeScript and survived to tell the story][coherentlabs] - Coherent Labs - using `grunt-ts`, jQuery and Kendo UI 195 | 196 | Contenido antiguo que posiblemente esté desactualizado 197 | 198 | - [Incrementally Migrating JS to TS][clayallsop] (old) 199 | - [Microsoft's TypeScript React Conversion Guide][mstsreactconversionguide] (old) 200 | 201 | ## Desde Flow 202 | 203 | - Try flow2ts: `npx flow2ts` - doesn't work 100% but saves some time ([mira este y otros tips de @braposo](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/79#issuecomment-458227322) at TravelRepublic) 204 | - [Incremental Migration to TypeScript on a Flowtype codebase][entria] at Entria 205 | - [MemSQL's Studio's migration](https://davidgom.es/porting-30k-lines-of-code-from-flow-to-typescript/) - blogpost con muchos tips útiles 206 | - Retail-UI's Codemod: https://github.com/skbkontur/retail-ui/tree/master/packages/react-ui-codemodes/flow-to-ts 207 | - Quick-n-dirty [Flow to TS Codemod](https://gist.github.com/skovhus/c57367ce6ecbc3f70bb7c80f25727a11) 208 | - [Ecobee's brief experience](https://mobile.twitter.com/alanhietala/status/1104450494754377728) 209 | - [Migrating a 50K SLOC Flow + React Native app to TypeScript](https://blog.usejournal.com/migrating-a-flow-react-native-app-to-typescript-c74c7bceae7d) 210 | 211 | ## Resultados 212 | 213 | - El número de despliegues de producción se duplicó para [Hootsuite][hootsuite] 214 | - Se encontraron globals accidentales para [Tiny][tiny] 215 | - Se encontraron llamadas a funciones incorrectas para [Tiny][tiny] 216 | - Se encontró un código de error poco utilizado que no se probó [Tiny][tiny] 217 | 218 | ## Estudios Académicos de Migración 219 | 220 | - [To Type or Not to Type: Quantifying Detectable Bugs in JavaScript](http://earlbarr.com/publications/typestudy.pdf) 221 | 222 | > Nuestro hallazgo central es que ambos sistemas de tipos estáticos encuentran un porcentaje importante de errores públicos: tanto Flow 0.30 como TypeScript 2.0 detectan con éxito el 15%. 223 | 224 | ## Diversas historias de migración de compañías notables y fuentes abiertas 225 | 226 | - [Adopting TypeScript at Scale - AirBnB's conversion story and strategy](https://www.youtube.com/watch?v=P-J9Eg7hJwE) 227 | - [Lyft](https://eng.lyft.com/typescript-at-lyft-64f0702346ea) 228 | - [Google](http://neugierig.org/software/blog/2018/09/typescript-at-google.html) 229 | - [Tiny][tiny] - [Talk from ForwardJS here](https://www.slideshare.net/tiny/porting-100k-lines-of-code-to-typescript) 230 | - [Slack](https://slack.engineering/typescript-at-slack-a81307fa288d) ([podcast](https://softwareengineeringdaily.com/2017/08/11/typescript-at-slack-with-felix-rieseberg/)) 231 | - [Historia de adopción de Netflix](https://www.youtube.com/watch?v=p5Hwb1YbNMY&feature=share) 232 | - [Priceline](https://medium.com/priceline-labs/trying-out-typescript-part-1-15a5267215b9) 233 | - Dropbox - [Talk at React Loop](https://www.youtube.com/watch?v=veXkJq0Z2Qk) 234 | 235 | Código abierto 236 | 237 | - [Jest's migration (PR)](https://github.com/facebook/jest/pull/7554#issuecomment-454358729) 238 | - [Expo's migration (issue)](https://github.com/expo/expo/issues/2164) 239 | - [Google Workbox migration](https://github.com/GoogleChrome/workbox/pull/2058) 240 | - [Atlassian's migration (PR)](https://github.com/atlassian/react-beautiful-dnd/issues/982) 241 | - [Yarn's migration (issue)](https://github.com/yarnpkg/yarn/issues/6953) 242 | - [React Native CLI](https://github.com/react-native-community/cli/issues/683) 243 | - [Next.js](https://nextjs.org/blog/next-9) 244 | - [Redux](https://github.com/reduxjs/redux/pull/3536) 245 | - [Dojo 1 -> 2 migration](https://devchat.tv/js-jabber/jsj-277-dojo-2-dylan-schiemann-kitson-kelly/) 246 | 247 | ## Enlaces 248 | 249 | [hootsuite]: https://medium.com/hootsuite-engineering/thoughts-on-migrating-to-typescript-5e1a04288202 "Thoughts on migrating to TypeScript" 250 | [clayallsop]: https://medium.com/@clayallsopp/incrementally-migrating-javascript-to-typescript-565020e49c88 "Incrementally Migrating JavaScript to TypeScript" 251 | [pleo]: https://medium.com/pleo/migrating-a-babel-project-to-typescript-af6cd0b451f4 "Migrating a Babel project to TypeScript" 252 | [mstsreactconversionguide]: https://github.com/Microsoft/TypeScript-React-Conversion-Guide "TypeScript React Conversion Guide" 253 | [entria]: https://medium.com/entria/incremental-migration-to-typescript-on-a-flowtype-codebase-515f6490d92d "Incremental Migration to TypeScript on a Flowtype codebase" 254 | [coherentlabs]: https://hashnode.com/post/how-we-migrated-a-200k-loc-project-to-typescript-and-survived-to-tell-the-story-ciyzhikcc0001y253w00n11yb "How we migrated a 200K+ LOC project to TypeScript and survived to tell the story" 255 | [tiny]: https://go.tiny.cloud/blog/benefits-of-gradual-strong-typing-in-javascript/ "Benefits of gradual strong typing in JavaScript" 256 | -------------------------------------------------------------------------------- /HOC.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | react + ts logo 11 | 12 | 13 |

Cheatsheets para desarrolladores experimentados de React que estan empezando con TypeScript

14 | 15 | [**Básico**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es#tabla-de-contenidos-de-la-cheatsheet-básica) | 16 | [**Avanzado**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/blob/master/AVANZADO.md) | 17 | [**Migrando**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/blob/master/MIGRANDO.md) | 18 | [**HOC**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/blob/master/HOC.md) | 19 | [**Inglés**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet) | 20 | [**中文翻译**](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) | 21 | [Contribuir](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/blob/master/CONTRIBUYENDO.md) | 22 | [Preguntas](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/issues/new) 23 | 24 |
25 | 26 | --- 27 | 28 | # HOC Cheatsheet 29 | 30 | **Este HOC Cheatsheet** agrupa todo el conocimiento disponible para escribir Componentes de Orden Superior (HOC por las siglas en inglés de higher-order component) con React y Typescript. 31 | 32 | - Inicialmente haremos un mapa detallado de la [documentación oficial sobre HOC](https://es.reactjs.org/docs/higher-order-components.html). 33 | - Si bien existen hooks, muchas librerías y bases de código aún necesitan escribir HOC 34 | - Render _props_ podrian ser considerado en el futuro 35 | - El objetivo es escribir HOC que ofrezcan un tipado seguro sin interferir 36 | 37 | --- 38 | 39 | ### HOC Cheatsheet Tabla de Contenido 40 | 41 |
42 | 43 | Expandir Tabla de Contenido 44 | 45 | - [Sección 0: Ejemplo completo de un HOC](#Sección-0-Ejemplo-completo-de-un-HOC) 46 | - [Sección 1: Documentación de React sobre HOC en TypeScript](#Sección-1-Documentación-de-React-sobre-HOC-en-TypeScript) 47 | - [Sección 2: Excluyendo Props](#sección-2-Excluyendo-Props) 48 | 49 |
50 | 51 | # Sección 0: Ejemplo completo de un HOC 52 | 53 | > Este es un ejemplo de un HOC para copiar y pegar. Si ciertos pedazos no tienen sentido para ti, ve a la [Sección 1](#Sección-1-Documentación-de-React-sobre-HOC-en-TypeScript) para obtener un tutorial detallado a través de una traducción completa de la documentación de React en Typescript. 54 | 55 | A veces quieres una forma sencilla de pasar _props_ desde otro lugar (ya sea el store global o un provider) y no quieres continuamente pasar los _props_ hacia abajo. Context es excelente para eso, pero entonces los valores desde el context solo pueden ser usado desde tu función `render`. Un HOC proveerá esos valores cómo _props_. 56 | 57 | **Los _props_ inyectados** 58 | 59 | ```ts 60 | interface WithThemeProps { 61 | primaryColor: string; 62 | } 63 | ``` 64 | 65 | **Uso en el componente** 66 | 67 | El objetivo es tener los _props_ disponibles en la interfaz para el componente, pero sustraído para los consumidores del componente cuando estén envuelto en el HoC. 68 | 69 | ```ts 70 | interface Props extends WithThemeProps { 71 | children: React.ReactNode; 72 | } 73 | 74 | class MyButton extends React.Component { 75 | public render() { 76 | // Renderiza un elemento usando el tema y otros props. 77 | } 78 | 79 | private someInternalMethod() { 80 | // Los valores del tema también estan aquí disponibles cómo props. 81 | } 82 | } 83 | 84 | export default withTheme(MyButton); 85 | ``` 86 | 87 | **Consumiendo el componente** 88 | 89 | Ahora, al consumir el componente puedes omitir el prop `primaryColor` o anular el que fue proporcionado a través del context. 90 | 91 | ```tsx 92 | Hello button // Válido 93 | Hello Button // Tambien válido 94 | ``` 95 | 96 | **Declarando el HoC** 97 | 98 | El HoC actual. 99 | 100 | ```tsx 101 | export function withTheme( 102 | WrappedComponent: React.ComponentType 103 | ) { 104 | // Intenta crear un buen displayName para React Dev Tools. 105 | const displayName = 106 | WrappedComponent.displayName || WrappedComponent.name || "Component"; 107 | 108 | // Creando el componente interno. El tipo de prop calculado aquí es donde ocurre la magia. 109 | return class ComponentWithTheme extends React.Component< 110 | Optionalize 111 | > { 112 | public static displayName = `withPages(${displayName})`; 113 | 114 | public render() { 115 | // Obten los props que quiere inyectar. Esto podría ser hecho con context. 116 | const themeProps = getThemePropsFromSomeWhere(); 117 | 118 | // this.props viene después para que puedan anular los props predeterminados. 119 | return ; 120 | } 121 | }; 122 | } 123 | ``` 124 | 125 | Tenga en cuenta que la aserción `{...this.props as T}` es necesaria debido a un error en TS 3.2 https://github.com/Microsoft/TypeScript/issues/28938#issuecomment-450636046 126 | 127 | Para obtener detalles de `Optionalize` consulte la [sección de tipos de utilidad](https://github.com/typescript-cheatsheets/typescript-utilities-cheatsheet#utility-types) 128 | 129 | Aquí hay un ejemplo más avanzado de un Componente Dinámico de Orden Superior (HOC por las siglas en inglés de higher-order component) que basa algunos de sus parámetros en los _props_ del componente que está siendo pasado. 130 | 131 | ```tsx 132 | // Inyecta valores estáticos a un componente de tal manera que siempre son proporcionados. 133 | export function inject( 134 | Component: React.JSXElementConstructor, 135 | injector: Pick 136 | ) { 137 | return function Injected(props: Omit) { 138 | return ; 139 | }; 140 | } 141 | ``` 142 | 143 | ### Usando `forwardRef` 144 | 145 | Para una reutilización "verdadera", también debes considerar exponer una referencia para tus HOC. Puedes utilizar `React.forwardRef` cómo está documentado en [el cheatsheet básico](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/blob/master/README.md#forwardrefcreateref), pero estamos interesados en más ejemplos del mundo real. [Aquí hay un buen ejemplo en práctica](https://gist.github.com/OliverJAsh/d2f462b03b3e6c24f5588ca7915d010e) de @OliverJAsh. 146 | 147 | # Sección 1: Documentación de React sobre HOC en TypeScript 148 | 149 | En esta primera sección nos referimos de cerca a [la documentación de React sobre HOC](https://es.reactjs.org/docs/higher-order-components.html) y ofrecemos un paralelo directo en TypeScript. 150 | 151 | ## Ejemplo de la documentación: [Usa HOCs para preocupaciones transversales](https://es.reactjs.org/docs/higher-order-components.html#usa-hocs-para-preocupaciones-transversales) 152 | 153 |
154 | 155 | 156 | Variables a las que se hace referencia en el siguiente ejemplo 157 | 158 | 159 | ```tsx 160 | /** Componentes hijos ficticios que pueden recibir cualquiera cosa */ 161 | const Comment = (_: any) => null; 162 | const TextBlock = Comment; 163 | 164 | /** Data ficticia */ 165 | type CommentType = { text: string; id: number }; 166 | const comments: CommentType[] = [ 167 | { 168 | text: "comment1", 169 | id: 1 170 | }, 171 | { 172 | text: "comment2", 173 | id: 2 174 | } 175 | ]; 176 | const blog = "blogpost"; 177 | 178 | /** simulación de la data */ 179 | const DataSource = { 180 | addChangeListener(e: Function) { 181 | // Hace algo 182 | }, 183 | removeChangeListener(e: Function) { 184 | // Hace algo 185 | }, 186 | getComments() { 187 | return comments; 188 | }, 189 | getBlogPost(id: number) { 190 | return blog; 191 | } 192 | }; 193 | /** Escriba alias solo para deduplicar */ 194 | type DataType = typeof DataSource; 195 | // type TODO_ANY = any; 196 | 197 | /** Tipos de utilidad que utilizamos */ 198 | type Omit = Pick>; 199 | // type Optionalize = Omit; 200 | 201 | /** Componentes reescritos de la documentación de React que solo utilizan props inyectados */ 202 | function CommentList({ data }: WithDataProps) { 203 | return ( 204 |
205 | {data.map((comment: CommentType) => ( 206 | 207 | ))} 208 |
209 | ); 210 | } 211 | interface BlogPostProps extends WithDataProps { 212 | id: number; 213 | // children: ReactNode; 214 | } 215 | function BlogPost({ data, id }: BlogPostProps) { 216 | return ( 217 |
218 | ; 219 |
220 | ); 221 | } 222 | ``` 223 | 224 | [Ver en TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCgeirhgAskBnJOFIuxuMHMJuiHABGrYADsAVkgxIAJsICenVgAkA8gGEK4mEiiZ0rAOrAGAERQwUABV5MAPABUAfHADeFOHFmWUALjhHAG44Gm9fOGB+AHMkMT1gNAoAX2paR0j+BlYYBTBWCExwqzS4a0zlbjs4QsqAdygUMHz5NFxIeLF4BksK8Uw9IllSjQrstgwAVxQAG0iuvQM0AqLxhqaWuDbwCE74AApJlnkYQWjGoW8kA0mZmFsIPjhsXEiYAEoKJAAPSFhnyZiDDAXZwOqmegAZUmQiYaCgwDAMBBYicABoynAfroxLJ+CZzL4HnwnM4MRpnPtPKFaAwonQ8qxZjMIHV+EcBPMBlAyhihJN4OcUJdxrl8jUikZGs05Bp2rs4vAWGB2JYkDMlOCGBABW95rp9AkxNEwRDKv09HFlhKytSpRtZfK9gFkOgYAA6ABSkIAGgBRGZIECKuViJgwKCTDDQezWVwAMjgGjR1LCLEDGAsVgqKABQNOPMw0ECqdoPEe-Hprtkuw1wmkKCOohg+H4RBQNbEdfETGAshWlTQMxQTCY1PT0hgWf8cCp5C8Xh8VkhOqgywCYqQtWnK8ma6QKfnC-LfBdqE7Gvs3p97oAMsAhI0oAoALIoMQoWKyACCMAjD4FZh7GTOA1BAUxYwxAAiJcUCg5wEOpd44AAXlcRwKGQjwjzCcYQE-RIKkYIgAmvO8HyfV930-ORf3-fldH4cEZjmKwAGsci4TcbXtFo5R2PY2FxOAiCYCAZgAN2bfh+xuO4qgrUs2GiFAe26LgT34WoCXoacMTqehEnoCoJCOdSCgRaJxFmTFuK1Yz8Fg-ARKDCApPkF48FMNskAAR0mYAiGDLoxyPbjiX4FC4DI+9H3YKiPy-OiEQYoCQLAiDrGg2D4OcIJqW4yErF0VD3GpRdfACYJqWSfKjyIGA9zELZh1HOAdOnLFvhxPFEGID1+I6RVYzsDEirVVxsIXLZdnDSNoygfZNICCKsPKhcmEmfJFs0946umrw6SYd16HfWRAw0U7jVYKKjpOs6Lqu2J3SEcRZH2I69vWw7DOO8M1VKqaDoqqwAgnTNfH2HdV2WDFdu+uBavW1JKCPLxtiGrozD7F8dS6Ur9mQtC4GhvdlndDtZEu99YnvcM4j0D7fvu3FHpppAvtR6aMYVLoTBYgBVMQQDx+AosJ1DnAR0n93dIK3KQanrrpnFGbuq7zsVp6Obq9aNbZ66CaJqW0YXO6WBgcbdH2IHgdgsH1Unacod8Xd9wxO74dNrxkk59aiFxRm1u9mlKjFcQTSLHkmB4c8I84KJ3U0zJ3VTuApOfGbwEDb53XrcMwRQJRLPoeAxFZMZBFMgvuNMNh+HfBQEbCWDTRYuBw2AduRAZfI0EYNAOOGEOGqa2cEa8exeL4p1FWKFAULcc3iqQd1YOSdxU-dJnE+TkchIUd4N6oE3gc56aUZ9-bQ9HqBmo63w6pR6gACoX7gdRRiOGjTQYJNZ5CnAF+VAvi-GgPANoYZ4D8WCjAFWOloSwnhIiZEoIor2UQXCBESIURzi8DAxUKtDxeBdsuGGSAAjTkcIyY2JNXbkPdLEGABCQqE0wrrcgPw-gQNmvAAAQiyaI1gIDhgQTCLBKCUSlQweI5BODdh4LgAIiAQiREwGIbOGW646FWGofkOGdgAgZRgPYZRqjwwRWyr4eCxt1paNXkwsxwjwxLTsO6PsnxyB7SAA) 225 | 226 |
227 | 228 | Ejemplo de un HOC de la documentación de React traducido a TypeScript 229 | 230 | ```tsx 231 | // Estos son los props que serán inyectados por el HOC 232 | interface WithDataProps { 233 | data: T; // data es genérico 234 | } 235 | // T es el tipo de data 236 | // P son los props del componente envuelto que es inferido 237 | // C es la interfaz real del componente envuelto (utilizado para tomar los defaultProps) 238 | export function withSubscription, C>( 239 | // este tipo nos permite inferir P, pero toma el tipo de componente envuelto por separado sin que interfiera con la inferencia en P 240 | WrappedComponent: React.JSXElementConstructor

& C, 241 | // selectData es un functor para T 242 | // los props son solo de lectura porque son solo lectura dentro de la clase 243 | selectData: ( 244 | dataSource: typeof DataSource, 245 | props: Readonly>> 246 | ) => T 247 | ) { 248 | // the magic is here: JSX.LibraryManagedAttributes will take the type of WrapedComponent and resolve its default props 249 | // against the props of WithData, which is just the original P type with 'data' removed from its requirements 250 | type Props = JSX.LibraryManagedAttributes>; 251 | type State = { 252 | data: T; 253 | }; 254 | return class WithData extends React.Component { 255 | constructor(props: Props) { 256 | super(props); 257 | this.handleChange = this.handleChange.bind(this); 258 | this.state = { 259 | data: selectData(DataSource, props) 260 | }; 261 | } 262 | 263 | componentDidMount = () => DataSource.addChangeListener(this.handleChange); 264 | 265 | componentWillUnmount = () => 266 | DataSource.removeChangeListener(this.handleChange); 267 | 268 | handleChange = () => 269 | this.setState({ 270 | data: selectData(DataSource, this.props) 271 | }); 272 | 273 | render() { 274 | // la escritura para difundir this.props es... muy compleja. La mejor manera de hacerlo ahora mismo es escribirlo cómo `any` 275 | // data todavia sera verificada 276 | return ( 277 | 278 | ); 279 | } 280 | }; 281 | // return WithData; 282 | } 283 | 284 | /** Uso de HOC con Componentes */ 285 | export const CommentListWithSubscription = withSubscription( 286 | CommentList, 287 | (DataSource: DataType) => DataSource.getComments() 288 | ); 289 | 290 | export const BlogPostWithSubscription = withSubscription( 291 | BlogPost, 292 | (DataSource: DataType, props: Omit) => 293 | DataSource.getBlogPost(props.id) 294 | ); 295 | ``` 296 | 297 | ## Ejemplo de documentación: [No mutes el componente original. Usa composición.](https://es.reactjs.org/docs/higher-order-components.html#no-mutes-el-componente-original-usa-composici%C3%B3n) 298 | 299 | Es bastante sencillo - asegurate de comprobar los _props_ recibidos cómo `T` [debido al error TS 3.2](https://github.com/Microsoft/TypeScript/issues/28938#issuecomment-450636046). 300 | 301 | ```tsx 302 | function logProps(WrappedComponent: React.ComponentType) { 303 | return class extends React.Component { 304 | componentWillReceiveProps( 305 | nextProps: React.ComponentProps 306 | ) { 307 | console.log("Current props: ", this.props); 308 | console.log("Next props: ", nextProps); 309 | } 310 | render() { 311 | // Envuelve el componente de entrada en un contenedor sin mutarlo. Muy bien! 312 | return ; 313 | } 314 | }; 315 | } 316 | ``` 317 | 318 | ## Ejemplo de la documentación: [Pasa los _props_ no relacionados al componente envuelto](https://es.reactjs.org/docs/higher-order-components.html#convenci%C3%B3n-pasa-los-props-no-relacionados-al-componente-envuelto) 319 | 320 | No se necesitan consejos específicos de Typescript aquí. 321 | 322 | ## Ejemplo de la documentación: [Maximizar la componibilidad](https://es.reactjs.org/docs/higher-order-components.html#convenci%C3%B3n-maximizar-la-componibilidad) 323 | 324 | los HOC pueden tomar la forma de funciones que retornan Componentes de Orden Superior que devuelven Componentes 325 | 326 | la función `connect` de `react-redux` tiene una serie de sobrecargas del que puedes obtener inspiración [fuente](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/bc0c933415466b34d2de5790f7cd6418f676801e/types/react-redux/v5/index.d.ts#L77) 327 | 328 | Construiremos nuestro propio `connect` para entender los HOC: 329 | 330 |

331 | 332 | 333 | Variables a las que se hace referencia en el siguiente ejemplo: 334 | 335 | 336 | ```tsx 337 | /** tipos de utilidad que utilizamos */ 338 | type Omit = Pick>; 339 | 340 | /** datos ficticios */ 341 | type CommentType = { text: string; id: number }; 342 | const comments: CommentType[] = [ 343 | { 344 | text: "comment1", 345 | id: 1 346 | }, 347 | { 348 | text: "comment2", 349 | id: 2 350 | } 351 | ]; 352 | /** componentes ficticios que reciben cualquier cosa */ 353 | const Comment = (_: any) => null; 354 | /** Componentes reescritos de la documentación de React que solo utilizan props de datos inyectados **/ 355 | function CommentList({ data }: WithSubscriptionProps) { 356 | return ( 357 |
358 | {data.map((comment: CommentType) => ( 359 | 360 | ))} 361 |
362 | ); 363 | } 364 | ``` 365 | 366 |
367 | 368 | ```tsx 369 | const commentSelector = (_: any, ownProps: any) => ({ 370 | id: ownProps.id 371 | }); 372 | const commentActions = () => ({ 373 | addComment: (str: string) => comments.push({ text: str, id: comments.length }) 374 | }); 375 | 376 | const ConnectedComment = connect(commentSelector, commentActions)(CommentList); 377 | // prop que son inyectadas por el HOC. 378 | interface WithSubscriptionProps { 379 | data: T; 380 | } 381 | function connect(mapStateToProps: Function, mapDispatchToProps: Function) { 382 | return function, C>( 383 | WrappedComponent: React.ComponentType 384 | ) { 385 | type Props = JSX.LibraryManagedAttributes>; 386 | // Creando el componente interno. El tipo de propiedades calculadas, es donde ocurre la magia 387 | return class ComponentWithTheme extends React.Component { 388 | public render() { 389 | // Obten los props que desea inyectar. Esto podría hacerse con context 390 | const mappedStateProps = mapStateToProps(this.state, this.props); 391 | const mappedDispatchProps = mapDispatchToProps(this.state, this.props); 392 | // this props viene después de tal modo que anula los predeterminados 393 | return ( 394 | 399 | ); 400 | } 401 | }; 402 | }; 403 | } 404 | ``` 405 | 406 | [Ver en el TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd5qQQkaY64BeOAbQF0A3BSq0GcAMK4WbADLAx3ABQBKLgD44iinDgAeACbAAbnAD0aisuHlq9RlNYwAykgA2SDNC6aA+gC44FBoATwAaOAgAdxoABRwwOgCg4NVODUUAb204YH0AqNj4ugA6XIoAX2UhG1F7ZkcAQQxgUW8VdU0s8h0UfX1JerYAxQYoANHgGgBzVI0maXZisABXOgALTLgYJAAPGHGYKHDcgPnHEvdpmDW4Soqq61sxSRoaD23+hzZvWzeMLW6cDObBc7k8R2ywJgTRgLXolkUAwWcgYD0o5FMpi2ayQdCQgSI2PxYCKWwgcAARvjJgArd5IfSU4JEuAACQA8uIKJNtlBMOh8QB1YDXJzLCl0NBQYBgWG0OIQBK6AAqGi6On0KBgKACyuq5QomGWNGatCBtD+MEUIBQYCc2u2yogCoSAQAYsbTTRwjawAAReRgLVoNZOl2JOAek1ymiqdVwIgwZZQGhwI3RuEq8IxOC7bY0fQcYWi8WS6WyuHhlVqcLiNQAnQ6QVQW1gBkDSBvIaIYgwYod2iOZXBNvV7Jx7I6GAj-Hh7wAKScAA1inIKS2oMEALJBFBTBkNGCHYAU5bbOi6cThdkgEW6GLhABEmu1j7UamqjbMWPERC1kymFlJjeKBzXAQc2GKOBlRxIEUFcNBllcLUGTgOdpzbOAcUJeQWUibD8WufEbSmYA0Cw1tWBKScEyQJMUyBZC6A4AcuxgYtQxxFhcz2VhCx7dA+1Yxx7yKNUaJ0FYKVcMjaILJAoHaeMvx0TFIzokMWRJRUOGCCBljgSIgngWl3igmDcOoJDGSpOB9EHQyRRuWxtj2HI7FQfRigkxsnngX0230e0ULnbhfWCx1nSKRRrnkYoGBQ8JYpKbSEjRFTfNqOAAoZAM6CDGAQ1C7LbTygqQzDaLkvih0kCStY4tSuh0oy79sUa0kmFxQJMF5IyoH4uhySIuDUwgIwFOlfRCNg6b+SQ+BB2owEMsTZNUwbVqdF0ZtKM+cC2J8jKMmKU7qqag0Vq2uATtOnKgtq8NLuuxtbuKe6yuDNYnqOxtzF+lqv2extyk-W59SAA) 407 | 408 | ## Ejemplo de la documentación: [Envuelve el nombre a mostrar para una depuración fácil](https://es.reactjs.org/docs/higher-order-components.html#convenci%C3%B3n-envuelve-el-nombre-a-mostrar-para-una-depuraci%C3%B3n-f%C3%A1cil) 409 | 410 | Este es bastante sencillo 411 | 412 | ```tsx 413 | interface WithSubscriptionProps { 414 | data: any; 415 | } 416 | 417 | function withSubscription< 418 | T extends WithSubscriptionProps = WithSubscriptionProps 419 | >(WrappedComponent: React.ComponentType) { 420 | class WithSubscription extends React.Component { 421 | /* ... */ 422 | public static displayName = `WithSubscription(${getDisplayName( 423 | WrappedComponent 424 | )})`; 425 | } 426 | return WithSubscription; 427 | } 428 | 429 | function getDisplayName(WrappedComponent: React.ComponentType) { 430 | return WrappedComponent.displayName || WrappedComponent.name || "Component"; 431 | } 432 | ``` 433 | 434 | ## No escrito: [sección de consideraciones](https://es.reactjs.org/docs/higher-order-components.html#consideraciones) 435 | 436 | - No utilice HOCs dentro del método render 437 | - Los métodos estáticos deben ser copiados 438 | - Las Refs no son pasadas 439 | 440 | # sección 2: Excluyendo Props 441 | 442 | Esto es cubierto en la sección 1 pero aquí nos centraremos en el ya que es un problema muy común. Los HOC a menudo inyectan _props_ a componentes pre-fabricados. El problema que queremos resolver es que el componente envuelto en HOC exponga un tipo que refleje el área de superficie reducida de los _props_ - sin tener que volver a escribir manualmente el HOC cada vez. Esto implica algunos genericos, afortunadamente con algunas utilidades auxiliares. 443 | 444 | Digamos que tenemos un componente 445 | 446 | ```tsx 447 | type DogProps { 448 | name: string 449 | owner: string 450 | } 451 | function Dog({name, owner}: DogProps) { 452 | return
Woof: {name}, Owner: {owner}
453 | } 454 | ``` 455 | 456 | Y tenemos un HOC `withOwner` que inyecta el `owner`: 457 | And we have a `withOwner` HOC that injects the `owner`: 458 | 459 | ```tsx 460 | const OwnedDog = withOwner("swyx")(Dog); 461 | ``` 462 | 463 | Queremos escribir `withOwner` de tal modo que pase por los tipos de cualquier componente cómo `Dog`, en el tipo de `OwnedDog`, menos la propiedad `owner` que inyecta: 464 | 465 | ```tsx 466 | typeof OwnedDog; // queremos que sea igual a { name: string } 467 | 468 | ; // este debe estar bien 469 | ; // este debe tener un typeError 470 | ; // este debe estar bien 471 | 472 | // y el HOC debe ser reusable por tipos de props totalmente diferentes! 473 | 474 | type CatProps = { 475 | lives: number; 476 | owner: string; 477 | }; 478 | function Cat({ lives, owner }: CatProps) { 479 | return ( 480 |
481 | {" "} 482 | Meow: {lives}, Owner: {owner} 483 |
484 | ); 485 | } 486 | 487 | const OwnedCat = withOwner("swyx")(Cat); 488 | 489 | ; // este debe estar bien 490 | ; // este debe tener un typeError 491 | ; // este debe estar bien 492 | ``` 493 | 494 | Entonces, cómo escribimos `withOwner`? 495 | 496 | 1. Obtenemos los tipos del componente: `keyof T` 497 | 2. Nosotros `Exclude` las propiedades que queremos encamascarar: `Exclude`, esto te deje con una lista de nombre de propiedades que quieres en el componente envuelto ejm: `name` 498 | 3. (Opcional) Utilice los tipo de intersección sí tiene mas para excluir: `Exclude` 499 | 4. Los nombres de las propiedades no son exactamente iguales a las propiedades en sí, los cuales también tienen un tipo asociado. Así que utilizamos esta lista generada de nombre para elegir `Pick` de los _props_ originales: `Pick>`, this leaves you with the new, filtered _props_, e.g. `{ name: string }` 500 | 5. (opcional) En lugar de escribir esto manualmente cada vez, podemos utilizar esta utilidad: `type Omit = Pick>` 501 | 6. Ahora escribimos el HOC cómo un función genérica: 502 | 503 | ```tsx 504 | function withOwner(owner: string) { 505 | return function( 506 | Component: React.ComponentType 507 | ) { 508 | return function(props: Omit): React.ReactNode { 509 | return ; 510 | }; 511 | }; 512 | } 513 | ``` 514 | 515 | _Nota: el de arriba es un ejemplo incompleto y no funcional. PR una solución!_ 516 | 517 | ## Aprende más 518 | 519 | Tendremos que extraer las lecciones de aquí en el futuro pero aquí estan: 520 | 521 | - https://medium.com/@xfor/typescript-react-hocs-context-api-cb46da611f12 522 | - https://medium.com/@jrwebdev/react-higher-order-component-patterns-in-typescript-42278f7590fb 523 | - https://www.matthewgerstman.com/tech/ts-tricks-higher-order-components/ 524 | -------------------------------------------------------------------------------- /IGNORATE.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | react + ts logo 11 | 12 | 13 |

Cheatsheets for experienced React developers getting started with TypeScript

14 | 15 | [**Basic**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#basic-cheatsheet-table-of-contents) | 16 | [**Advanced**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md) | 17 | [**Migrating**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/MIGRATING.md) | 18 | [**HOC**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/HOC.md) | 19 | [中文翻译](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) | 20 | [**Español**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es) | 21 | [Contribute!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/CONTRIBUTING.md) | 22 | [Ask!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new/choose) 23 | 24 |
25 | 26 | --- 27 | 28 | # Advanced Cheatsheet 29 | 30 | **This Advanced Cheatsheet** helps show and explain advanced usage of generic types for people writing reusable type utilities/functions/render prop/higher order components and TS+React **libraries**. 31 | 32 | - It also has miscellaneous tips and tricks for pro users. 33 | - Advice for contributing to DefinitelyTyped 34 | - The goal is to take _full advantage_ of TypeScript. 35 | 36 | **Creating React + TypeScript Libraries** 37 | 38 | The best tool for creating React + TS libraries right now is [`tsdx`](https://github.com/palmerhq/tsdx). Run `npx tsdx create` and select the "react" option. You can view [the React User Guide](https://github.com/palmerhq/tsdx/issues/5) for a few tips on React+TS library best practices and optimizations for production. 39 | 40 | - Be sure to also check [`basarat`'s guide](https://basarat.gitbooks.io/typescript/content/docs/quick/library.html) for library tsconfig settings. 41 | - From the Angular world, check out https://github.com/bitjson/typescript-starter 42 | 43 | --- 44 | 45 | ### Advanced Cheatsheet Table of Contents 46 | 47 |
48 | 49 | Expand Table of Contents 50 | 51 | - [Section 0: Utility Types](#section-0-utility-types) 52 | - [Section 1: Reusable Components/Type Utilities](#section-1-reusable-componentstype-utilities) 53 | - [Higher Order Components](#higher-order-components-hocs) 54 | - [Render Props](#render-props) 55 | - [Conditionally Rendering Components](#conditinonally-rendering-components) 56 | - [`as` props (passing a component to be rendered)](#as-props-passing-a-component-to-be-rendered) 57 | - [Generic Components](#generic-components) 58 | - [Typing a Component that Accepts Different Props](#typing-a-component-that-accepts-different-props) 59 | - [Props: One or the Other but not Both](#props-one-or-the-other-but-not-both) 60 | - [Props: Must Pass Both](#props-must-pass-both) 61 | - [Props: Can Optionally Pass One Only If the Other Is Passed](#props-can-optionally-pass-one-only-if-the-other-is-passed) 62 | - [Omit attribute from a type](#omit-attribute-from-a-type) 63 | - [Type Zoo](#type-zoo) 64 | - [Extracting Prop Types of a Component](#extracting-prop-types-of-a-component) 65 | - [Handling Exceptions](#handling-exceptions) 66 | - [Third Party Libraries](#third-party-libraries) 67 | - [Section 2: Useful Patterns by TypeScript Version](#section-2-useful-patterns-by-typescript-version) 68 | - [TypeScript 2.9](#typescript-29) 69 | - [TypeScript 3.0](#typescript-30) 70 | - [TypeScript 3.1](#typescript-31) 71 | - [TypeScript 3.2](#typescript-32) 72 | - [TypeScript 3.3](#typescript-33) 73 | - [TypeScript 3.4](#typescript-34) 74 | - [TypeScript 3.5](#typescript-35) 75 | - [TypeScript 3.6](#typescript-36) 76 | - [TypeScript 3.7](#typescript-37) 77 | - [Section 3: Misc. Concerns](#section-3-misc-concerns) 78 | - [Writing TypeScript Libraries instead of Apps](#writing-typescript-libraries-instead-of-apps) 79 | - [Commenting Components](#commenting-components) 80 | - [Namespaced Components](#namespaced-components) 81 | - [Design System Development](#design-system-development) 82 | - [Migrating from Flow](#migrating-from-flow) 83 | - [Prettier](#prettier) 84 | - [Testing](#testing) 85 | - [Linting](#linting) 86 | - [Working with Non-TypeScript Libraries (writing your own index.d.ts)](#working-with-non-typescript-libraries-writing-your-own-indexdts) 87 | - [Section 4: @types/react and @types/react-dom APIs](#section-4-typesreact-and-typesreact-dom-apis) 88 | 89 | - [Adding non-standard attributes](#adding-non-standard-attributes) 90 |
91 | 92 | # Section 0: Utility Types 93 | 94 | We will assume knowledge of utility types covered in the sister project [`typescript-utilities-guide`](https://github.com/typescript-cheatsheets/typescript-utilities-guide). Look up libraries included there as well for your typing needs. 95 | 96 | # Section 1: Advanced Guides 97 | 98 | ## Higher Order Components (HOCs) 99 | 100 | **There is now a dedicated [HOC cheatsheet](./HOC.md) you can refer to get some practice on HOCs.** 101 | 102 | ## Render Props 103 | 104 | Sometimes you will want to write a function that can take a React element or a string or something else as a prop. The best Type to use for such a situation is `React.ReactNode` which fits anywhere a normal, well, React Node would fit: 105 | 106 | ```tsx 107 | export interface Props { 108 | label?: React.ReactNode; 109 | children: React.ReactNode; 110 | } 111 | export const Card = (props: Props) => { 112 | return ( 113 |
114 | {props.label &&
{props.label}
} 115 | {props.children} 116 |
117 | ); 118 | }; 119 | ``` 120 | 121 | If you are using a function-as-a-child render prop: 122 | 123 | ```tsx 124 | export interface Props { 125 | children: (foo: string) => React.ReactNode; 126 | } 127 | ``` 128 | 129 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new/choose). 130 | 131 | ## Conditionally Rendering Components 132 | 133 | Use [type guards](https://basarat.gitbooks.io/typescript/docs/types/typeGuard.html#user-defined-type-guards)! 134 | 135 | ```tsx 136 | // Button props 137 | type ButtonProps = React.ButtonHTMLAttributes & { 138 | href?: undefined; 139 | }; 140 | 141 | // Anchor props 142 | type AnchorProps = React.AnchorHTMLAttributes & { 143 | href?: string; 144 | }; 145 | 146 | // Input/output options 147 | type Overload = { 148 | (props: ButtonProps): JSX.Element; 149 | (props: AnchorProps): JSX.Element; 150 | }; 151 | 152 | // Guard to check if href exists in props 153 | const hasHref = (props: ButtonProps | AnchorProps): props is AnchorProps => 154 | "href" in props; 155 | 156 | // Component 157 | const Button: Overload = (props: ButtonProps | AnchorProps) => { 158 | // anchor render 159 | if (hasHref(props)) return ; 160 | // button render 161 | return 172 | {/* 😭 Error, `disabled` doesnt exist on anchor element */} 173 | 176 | 177 | ); 178 | } 179 | ``` 180 | 181 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoAekrgCEBXGGCAOzjBzAGcKYBPMEjqNmLAAqcucALyJiMAHQMmrABIAVALIAZAIJMowAEaMkXADwady0QFEANkhBIWMAHxwAZHADeFOHAAFkSYAPwAXHD0LAAmSJjALEgxANwUAL5p5BTUcLosaIHQ7JK8AkL5hdASENwycuiKlUVQVnoGxqYWbc3QDk4u7l6+-kEhEXBcMIYsAOZZmRQ5NACSLGCMlBCMG-C1MMCsPOT8gnAA8gBuSFD2ECgx9X7kAQAUHLVckTasNdwAlJEAFIAZQAGgp+s5XFk3h9uJFelA-lxAXBQRCoYMFlllnAAOL0FBQR7MOCFJBoADWcGAmDG8TgSAAHsAplJEiVPhQ0Ed4IEUFxVCF6u9JN8RL9JHAAD55AotFFo+EcqRIlEyNyjABEwXi2tpbBVuKoNAAwrhIElXDy+cIVCxIlcbncHqKVRKHRq5erJP9NSMXnBcigFcUiLEbqM6XBXgKhSExZ9-v6iDB6FA2OYUL4FHmVelg25YcGaCYHXAI3EoKM0xms+XRLn85JC5RixkTbkAKpcFCzJAUTDRDCHNi6MBgV7+54BOuZ2OjALmLVBgIBHyUABUcEAvBuAOD28vZ7HBZhAII8t5R0kv1+YfmwYMSBzBpNqAPpGeyhqkGvWYN9AiYBFqAAd3AhQzwgWZHAUXkQG1Vd12QuB1DMGBb2XSgHyQlDNx3XdAFo9uBbCgHAoAAGjgAADGI2RQL9kmouAYggMxXCZVkpjgVg4FDKooCZRxoXgK8bzXO8HxY+jGMef832ZRDMPXNCpmU8xsMlFhcKw3D-gWIA) 182 | 183 | ## `as` props (passing a component to be rendered) 184 | 185 | `ElementType` is pretty useful to cover most types that can be passed to createElement e.g. 186 | 187 | ```tsx 188 | function PassThrough(props: { as: React.ElementType }) { 189 | const { as: Component } = props; 190 | 191 | return ; 192 | } 193 | ``` 194 | 195 | [Thanks @eps1lon](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/69) for this 196 | 197 | ## Generic Components 198 | 199 | Just as you can make generic functions and classes in TypeScript, you can also make generic components to take advantage of the type system for reusable type safety. Both Props and State can take advantage of the same generic types, although it probably makes more sense for Props than for State. You can then use the generic type to annotate types of any variables defined inside your function / class scope. 200 | 201 | ```tsx 202 | interface Props { 203 | items: T[]; 204 | renderItem: (item: T) => React.ReactNode; 205 | } 206 | function List(props: Props) { 207 | const { items, renderItem } = props; 208 | const [state, setState] = React.useState([]); // You can use type T in List function scope. 209 | return ( 210 |
211 | {items.map(renderItem)} 212 | 213 | {JSON.stringify(state, null, 2)} 214 |
215 | ); 216 | } 217 | ``` 218 | 219 | You can then use the generic components and get nice type safety through type inference: 220 | 221 | ```tsx 222 | ReactDOM.render( 223 | ( 226 |
  • 227 | {item.toPrecision(3)} // Error: Property 'toPrecision' does not exist on 228 | type 'string'. 229 |
  • 230 | )} 231 | />, 232 | document.body 233 | ); 234 | ``` 235 | 236 | As of [TS 2.9](#typescript-29), you can also supply the type parameter in your JSX to opt out of type inference: 237 | 238 | ```tsx 239 | ReactDOM.render( 240 | 241 | items={["a", "b"]} // Error: Type 'string' is not assignable to type 'number'. 242 | renderItem={item =>
  • {item.toPrecision(3)}
  • } 243 | />, 244 | document.body 245 | ); 246 | ``` 247 | 248 | You can also use Generics using fat arrow function style: 249 | 250 | ```tsx 251 | interface Props { 252 | items: T[]; 253 | renderItem: (item: T) => React.ReactNode; 254 | } 255 | 256 | // Note the before the function definition. 257 | // You can't use just `` as it will confuse the TSX parser whether it's a JSX tag or a Generic Declaration. 258 | // You can also use https://github.com/microsoft/TypeScript/issues/15713#issuecomment-499474386 259 | const List = (props: Props) => { 260 | const { items, renderItem } = props; 261 | const [state, setState] = React.useState([]); // You can use type T in List function scope. 262 | return ( 263 |
    264 | {items.map(renderItem)} 265 | 266 | {JSON.stringify(state, null, 2)} 267 |
    268 | ); 269 | }; 270 | ``` 271 | 272 | The same for using classes: (Credit: [Karol Majewski](https://twitter.com/WrocTypeScript/status/1163234064343736326)'s [gist](https://gist.github.com/karol-majewski/befaf05af73c7cb3248b4e084ae5df71)) 273 | 274 | ```tsx 275 | interface Props { 276 | items: T[]; 277 | renderItem: (item: T) => React.ReactNode; 278 | } 279 | 280 | interface State { 281 | items: T[]; 282 | } 283 | 284 | class List extends React.PureComponent, State> { 285 | // You can use type T inside List class. 286 | state: Readonly> = { 287 | items: [] 288 | }; 289 | render() { 290 | const { items, renderItem } = this.props; 291 | // You can use type T inside List class. 292 | const clone: T[] = items.slice(0); 293 | return ( 294 |
    295 | {items.map(renderItem)} 296 | 297 | {JSON.stringify(this.state, null, 2)} 298 |
    299 | ); 300 | } 301 | } 302 | ``` 303 | 304 | Though you can't use Generic Type Parameters for Static Members: 305 | 306 | ```tsx 307 | class List extends React.PureComponent, State> { 308 | // Static members cannot reference class type parameters.ts(2302) 309 | static getDerivedStateFromProps(props: Props, state: State) { 310 | return { items: props.items }; 311 | } 312 | } 313 | ``` 314 | 315 | To fix this you need to convert your static function to a type inferred function: 316 | 317 | ```tsx 318 | class List extends React.PureComponent, State> { 319 | static getDerivedStateFromProps(props: Props, state: State) { 320 | return { items: props.items }; 321 | } 322 | } 323 | ``` 324 | 325 | ### Generic components with children 326 | 327 | `children` is usually not defined as a part of the props type. Unless `children` are explicitly defined as a part of the `props` type, an attempt to use `props.children` in JSX or in the function body will fail: 328 | 329 | ```tsx 330 | interface WrapperProps { 331 | item: T; 332 | renderItem: (item: T) => React.ReactNode; 333 | } 334 | 335 | /* Property 'children' does not exist on type 'WrapperProps'. */ 336 | const Wrapper = (props: WrapperProps) => { 337 | return ( 338 |
    339 | {props.renderItem(props.item)} 340 | {props.children} 341 |
    342 | ); 343 | }; 344 | 345 | /* 346 | Type '{ children: string; item: string; renderItem: (item: string) => string; }' is not assignable to type 'IntrinsicAttributes & WrapperProps'. 347 | Property 'children' does not exist on type 'IntrinsicAttributes & WrapperProps'. 348 | */ 349 | 350 | const wrapper = ( 351 | item}> 352 | {test} 353 | 354 | ); 355 | ``` 356 | 357 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgHUoUwx6AFHMAZwA8AFQB8cAN4U4cYHRAAuOMIDc0uEWoATegEl5SgBRyki5QEo4AXnHJ0MAHR2MAOQg615GWgAWwADZamkrOjqFuHhQAvhQUAPQAVHC8EFywAJ4EvgFBSNT4cFoQSPxw1BDwSAAewPzwENRwMOlcBGwcaSkCIqL4DnAJcRRoDXWs7Jz01nAicNV02qUSUaKGYHz8Su2TUF1CYpY2kupEMACuUI2G6jKCWsAAbqI3MpLrqfwOmjpQ+qZrGwcJhA5hiXleMgk7wEDmygU0YIhgji9ye6nMniinniCQowhazHwEjgcNy1CUdSgNAA5ipZAY4JSaXTvnoGcYGUzqNTDuIubS4FECrUyhU4Ch+PxgNTqCgAEb+ZgwCBNAkEXS0KnUKVoACCMBgVLlZzopQAZOMOjwNoJ+b0HOouvRmlk-PC8gUiiVRZUamMGqrWvgNYaaDr9aHjaa4Bbtp0bXa+hRBrFyCNtfBTfArHBDLyZqjRAAJJD+fwqrPIwvDUbwADuEzS02u4MEcamwKsACIs12NHkfn8QFYJMDrOJgSsXhIs4iZnF21BnuQMUA) 358 | 359 | To work around that, either add `children` to the `WrapperProps` definition (possibly narrowing down its type, as needed): 360 | 361 | ```tsx 362 | interface WrapperProps { 363 | item: T; 364 | renderItem: (item: T) => React.ReactNode; 365 | children: string; // The component will only accept a single string child 366 | } 367 | 368 | const Wrapper = (props: WrapperProps) => { 369 | return ( 370 |
    371 | {props.renderItem(props.item)} 372 | {props.children} 373 |
    374 | ); 375 | }; 376 | ``` 377 | 378 | or wrap the type of the props in `React.PropsWithChildren` (this is what `React.FC<>` does): 379 | 380 | ```tsx 381 | interface WrapperProps { 382 | item: T; 383 | renderItem: (item: T) => React.ReactNode; 384 | } 385 | 386 | const Wrapper = ( 387 | props: React.PropsWithChildren> 388 | ) => { 389 | return ( 390 |
    391 | {props.renderItem(props.item)} 392 | {props.children} 393 |
    394 | ); 395 | }; 396 | ``` 397 | 398 | ## Typing a Component that Accepts Different Props 399 | 400 | Components, and JSX in general, are analogous to functions. When a component can render differently based on their props, it's similar to how a function can be overloaded to have multiple call signatures. In the same way, you can overload a function component's call signature to list all of its different "versions". 401 | 402 | A very common use case for this is to render something as either a button or an anchor, based on if it receives a `href` attribute. 403 | 404 | ```tsx 405 | type ButtonProps = JSX.IntrinsicElements["button"]; 406 | type AnchorProps = JSX.IntrinsicElements["a"]; 407 | 408 | // optionally use a custom type guard 409 | function isPropsForAnchorElement( 410 | props: ButtonProps | AnchorProps 411 | ): props is AnchorProps { 412 | return "href" in props; 413 | } 414 | 415 | function Clickable(props: ButtonProps | AnchorProps) { 416 | if (isPropsForAnchorElement(props)) { 417 | return
    ; 418 | } else { 419 | return 172 | {/* 😭 Error, `disabled` no existe en el elemento de anlaze */} 173 | 176 | 177 | ); 178 | } 179 | ``` 180 | 181 | [Ver en TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoAekrgCEBXGGCAOzjBzAGcKYBPMEjqNmLAAqcucALyJiMAHQMmrABIAVALIAZAIJMowAEaMkXADwady0QFEANkhBIWMAHxwAZHADeFOHAAFkSYAPwAXHD0LAAmSJjALEgxANwUAL5p5BTUcLosaIHQ7JK8AkL5hdASENwycuiKlUVQVnoGxqYWbc3QDk4u7l6+-kEhEXBcMIYsAOZZmRQ5NACSLGCMlBCMG-C1MMCsPOT8gnAA8gBuSFD2ECgx9X7kAQAUHLVckTasNdwAlJEAFIAZQAGgp+s5XFk3h9uJFelA-lxAXBQRCoYMFlllnAAOL0FBQR7MOCFJBoADWcGAmDG8TgSAAHsAplJEiVPhQ0Ed4IEUFxVCF6u9JN8RL9JHAAD55AotFFo+EcqRIlEyNyjABEwXi2tpbBVuKoNAAwrhIElXDy+cIVCxIlcbncHqKVRKHRq5erJP9NSMXnBcigFcUiLEbqM6XBXgKhSExZ9-v6iDB6FA2OYUL4FHmVelg25YcGaCYHXAI3EoKM0xms+XRLn85JC5RixkTbkAKpcFCzJAUTDRDCHNi6MBgV7+54BOuZ2OjALmLVBgIBHyUABUcEAvBuAOD28vZ7HBZhAII8t5R0kv1+YfmwYMSBzBpNqAPpGeyhqkGvWYN9AiYBFqAAd3AhQzwgWZHAUXkQG1Vd12QuB1DMGBb2XSgHyQlDNx3XdAFo9uBbCgHAoAAGjgAADGI2RQL9kmouAYggMxXCZVkpjgVg4FDKooCZRxoXgK8bzXO8HxY+jGMef832ZRDMPXNCpmU8xsMlFhcKw3D-gWIA) 182 | 183 | ## `as` props (pasando un componente para ser renderizado) 184 | 185 | `ElementType` es bastante útil para cubrir la mayoría de los tipos que se pueden pasar a createElement, p.ej. 186 | 187 | ```tsx 188 | function PassThrough(props: { as: React.ElementType }) { 189 | const { as: Component } = props; 190 | 191 | return ; 192 | } 193 | ``` 194 | 195 | [Gracias @eps1lon](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/69) por esto. 196 | 197 | ## Componentes genéricos 198 | 199 | Del mismo modo que puede crear funciones y clases genéricas en TypeScript, también puede crear componentes genéricos para aprovechar el sistema de tipos para la seguridad de _tipos_ reutilizables. Tanto Props como State pueden aprovechar los mismos _tipos_ genéricos, aunque probablemente tenga más sentido para Props que para State. Luego puede usar el tipo genérico para anotar los _tipos_ de cualquier variable definida dentro del alcance de tufunción / clase. 200 | 201 | ```tsx 202 | interface Props { 203 | items: T[]; 204 | renderItem: (item: T) => React.ReactNode; 205 | } 206 | function List(props: Props) { 207 | const { items, renderItem } = props; 208 | const [state, setState] = React.useState([]); // Puede usar el tipo T en el alcance de la función Lista. 209 | return ( 210 |
    211 | {items.map(renderItem)} 212 | 213 | {JSON.stringify(state, null, 2)} 214 |
    215 | ); 216 | } 217 | ``` 218 | 219 | A continuación, puede utilizar los componentes genéricos y obtener una buena seguridad de _tipos_ mediante la inferencia de _tipos_: 220 | 221 | ```tsx 222 | ReactDOM.render( 223 | ( 226 |
  • 227 | {item.toPrecision(3)} // Error: la propiedad 'toPrecision' no existe en 228 |          escriba 'string'. 229 |
  • 230 | )} 231 | />, 232 | document.body 233 | ); 234 | ``` 235 | 236 | A partir de [TS 2.9](#typescript-29), también puede proporcionar el parámetro de _tipo_ en tuJSX para optar por la inferencia de _tipos_: 237 | 238 | ```tsx 239 | ReactDOM.render( 240 | 241 | items={["a", "b"]} // Error: el tipo 'string' no es asignable para escribir 'number'. 242 | renderItem={item =>
  • {item.toPrecision(3)}
  • } 243 | />, 244 | document.body 245 | ); 246 | ``` 247 | 248 | También puede usar genéricos usando el estilo de función de flecha de grande: 249 | 250 | ```tsx 251 | interface Props { 252 | items: T[]; 253 | renderItem: (item: T) => React.ReactNode; 254 | } 255 | 256 | // Tenga en cuenta que antes de la definición de la función. 257 | // No puedes usar solo `` ya que confundirá al analizador TSX ya sea una etiqueta JSX o una Declaración Genérica. 258 | // También puedes usar https://github.com/microsoft/TypeScript/issues/15713#issuecomment-499474386 259 | const List = (props: Props) => { 260 | const { items, renderItem } = props; 261 | const [state, setState] = React.useState([]); // Puedes usar el tipo T en el alcance de la función Lista. 262 | return ( 263 |
    264 | {items.map(renderItem)} 265 | 266 | {JSON.stringify(state, null, 2)} 267 |
    268 | ); 269 | }; 270 | ``` 271 | 272 | Lo mismo para usar clases: (Crédito: al [gist](https://gist.github.com/karol-majewski/befaf05af73c7cb3248b4e084ae5df71) de [Karol Majewski](https://twitter.com/WrocTypeScript/status/1163234064343736326)). 273 | 274 | ```tsx 275 | interface Props { 276 | items: T[]; 277 | renderItem: (item: T) => React.ReactNode; 278 | } 279 | 280 | interface State { 281 | items: T[]; 282 | } 283 | 284 | class List extends React.PureComponent, State> { 285 | // Puedes usar el tipo T dentro de la clase Lista. 286 | state: Readonly> = { 287 | items: [] 288 | }; 289 | render() { 290 | const { items, renderItem } = this.props; 291 | // Puedes usar el tipo T dentro de la clase Lista. 292 | const clone: T[] = items.slice(0); 293 | return ( 294 |
    295 | {items.map(renderItem)} 296 | 297 | {JSON.stringify(this.state, null, 2)} 298 |
    299 | ); 300 | } 301 | } 302 | ``` 303 | 304 | Aunque no puedes utilizar los parámetros de tipo genérico para miembros estáticos: 305 | 306 | ```tsx 307 | class List extends React.PureComponent, State> { 308 | // Los miembros estáticos no pueden hacer referencia a los parámetros de tipo de clase.ts(2302) 309 | static getDerivedStateFromProps(props: Props, state: State) { 310 | return { items: props.items }; 311 | } 312 | } 313 | ``` 314 | 315 | Para solucionar esto, debe convertir tu función estática en una función inferida de tipo: 316 | 317 | ```tsx 318 | class List extends React.PureComponent, State> { 319 | static getDerivedStateFromProps(props: Props, state: State) { 320 | return { items: props.items }; 321 | } 322 | } 323 | ``` 324 | 325 | ### Componentes genéricos con children 326 | 327 | `children` generalmente no se define como parte del tipo de _props_. A menos que `children` se defina explícitamente como parte del tipo `props`, un intento de usar `props.children` en JSX o en el cuerpo de la función fallará: 328 | 329 | ```tsx 330 | interface WrapperProps { 331 | item: T; 332 | renderItem: (item: T) => React.ReactNode; 333 | } 334 | 335 | /* La propiedad 'children' no existe en el tipo 'WrapperProps '. */ 336 | const Wrapper = (props: WrapperProps) => { 337 | return ( 338 |
    339 | {props.renderItem(props.item)} 340 | {props.children} 341 |
    342 | ); 343 | }; 344 | 345 | /* 346 | Type '{ children: string; item: string; renderItem: (item: string) => string; }' no es asignable a tipo 'IntrinsicAttributes & WrapperProps'. 347 | La propiedad 'children' no existe en el tipo'IntrinsicAttributes & WrapperProps'. 348 | */ 349 | 350 | const wrapper = ( 351 | item}> 352 | {test} 353 | 354 | ); 355 | ``` 356 | 357 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgHUoUwx6AFHMAZwA8AFQB8cAN4U4cYHRAAuOMIDc0uEWoATegEl5SgBRyki5QEo4AXnHJ0MAHR2MAOQg615GWgAWwADZamkrOjqFuHhQAvhQUAPQAVHC8EFywAJ4EvgFBSNT4cFoQSPxw1BDwSAAewPzwENRwMOlcBGwcaSkCIqL4DnAJcRRoDXWs7Jz01nAicNV02qUSUaKGYHz8Su2TUF1CYpY2kupEMACuUI2G6jKCWsAAbqI3MpLrqfwOmjpQ+qZrGwcJhA5hiXleMgk7wEDmygU0YIhgji9ye6nMniinniCQowhazHwEjgcNy1CUdSgNAA5ipZAY4JSaXTvnoGcYGUzqNTDuIubS4FECrUyhU4Ch+PxgNTqCgAEb+ZgwCBNAkEXS0KnUKVoACCMBgVLlZzopQAZOMOjwNoJ+b0HOouvRmlk-PC8gUiiVRZUamMGqrWvgNYaaDr9aHjaa4Bbtp0bXa+hRBrFyCNtfBTfArHBDLyZqjRAAJJD+fwqrPIwvDUbwADuEzS02u4MEcamwKsACIs12NHkfn8QFYJMDrOJgSsXhIs4iZnF21BnuQMUA) 358 | 359 | Para evitar eso, agregue `children` a la definición de`WrapperProps` (posiblemente reduzca tutipo, según sea necesario): 360 | 361 | ```tsx 362 | interface WrapperProps { 363 | item: T; 364 | renderItem: (item: T) => React.ReactNode; 365 | children: string; // El componente solo aceptará una sola cadena strig child 366 | } 367 | 368 | const Wrapper = (props: WrapperProps) => { 369 | return ( 370 |
    371 | {props.renderItem(props.item)} 372 | {props.children} 373 |
    374 | ); 375 | }; 376 | ``` 377 | 378 | o envuelva el tipo de los props en `React.PropsWithChildren` (this is what `React.FC<>` does): 379 | 380 | ```tsx 381 | interface WrapperProps { 382 | item: T; 383 | renderItem: (item: T) => React.ReactNode; 384 | } 385 | 386 | const Wrapper = ( 387 | props: React.PropsWithChildren> 388 | ) => { 389 | return ( 390 |
    391 | {props.renderItem(props.item)} 392 | {props.children} 393 |
    394 | ); 395 | }; 396 | ``` 397 | 398 | ## Escribiendo un componente que acepta diferentes accesorios 399 | 400 | Los componentes, y JSX en general, son análogos a las funciones. Cuando un componente puede renderizarse de manera diferente en función de sus _props_, es similar a cómo se puede sobrecargar una función para tener múltiples _llamadas_. Del mismo modo, puede sobrecargar las _llamadas_ de un componente de función para enumerar todas sus diferentes "versiones". 401 | 402 | Un caso de uso muy común para esto es representar algo como un botón o un ancla, en función de si recibe un atributo `href`. 403 | 404 | ```tsx 405 | type ButtonProps = JSX.IntrinsicElements["button"]; 406 | type AnchorProps = JSX.IntrinsicElements["a"]; 407 | 408 | // opcionalmente use un type guard personalizado 409 | function isPropsForAnchorElement( 410 | props: ButtonProps | AnchorProps 411 | ): props is AnchorProps { 412 | return "href" in props; 413 | } 414 | 415 | function Clickable(props: ButtonProps | AnchorProps) { 416 | if (isPropsForAnchorElement(props)) { 417 | return
    ; 418 | } else { 419 | return