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 =
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 |
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 |
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 |
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 |
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 |
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 ;
162 | };
163 |
164 | // Usage
165 | function App() {
166 | return (
167 | <>
168 | {/* 😎 All good */}
169 |
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 |
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 |
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 |
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 ;
420 | }
421 | }
422 | ```
423 |
424 | They don't even need to be completely different props, as long as they have at least one difference in properties:
425 |
426 | ```tsx
427 | type LinkProps = Omit & { to?: string };
428 |
429 | function RouterLink(props: LinkProps | AnchorProps) {
430 | if ("to" in props) {
431 | return ;
432 | } else {
433 | return ;
434 | }
435 | }
436 | ```
437 |
438 |
439 | Approach: Generic Components
440 |
441 | Here is an example solution, see the further discussion for other solutions. _thanks to [@jpavon](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/12#issuecomment-394440577)_
442 |
443 | ```tsx
444 | interface LinkProps {}
445 | type AnchorProps = React.AnchorHTMLAttributes;
446 | type RouterLinkProps = Omit;
447 |
448 | const Link = (
449 | props: LinkProps & T extends RouterLinkProps ? RouterLinkProps : AnchorProps
450 | ) => {
451 | if ((props as RouterLinkProps).to) {
452 | return ;
453 | } else {
454 | return ;
455 | }
456 | };
457 |
458 | to="/">My link; // ok
459 | href="/">My link; // ok
460 | to="/" href="/">
461 | My link
462 | ; // error
463 | ```
464 |
465 |
466 |
467 |
468 | Approach: Composition
469 |
470 | If you want to conditionally render a component, sometimes is better to use [React's composition model](https://reactjs.org/docs/composition-vs-inheritance.html) to have simpler components and better to understand typings:
471 |
472 | ```tsx
473 | type AnchorProps = React.AnchorHTMLAttributes
474 | type RouterLinkProps = Omit
475 |
476 | interface Button {
477 | as: React.ComponentClass | 'a'
478 | }
479 |
480 | const Button: React.FunctionComponent
501 |
502 | You may also want to use Discriminated Unions, please check out [Expressive React Component APIs with Discriminated Unions](https://blog.andrewbran.ch/expressive-react-component-apis-with-discriminated-unions/).
503 |
504 | Here is a brief intuition for **Discriminated Union Types**:
505 |
506 | ```ts
507 | type UserTextEvent = { value: string; target: HTMLInputElement };
508 | type UserMouseEvent = { value: [number, number]; target: HTMLElement };
509 | type UserEvent = UserTextEvent | UserMouseEvent;
510 | function handle(event: UserEvent) {
511 | if (typeof event.value === "string") {
512 | event.value; // string
513 | event.target; // HTMLInputElement | HTMLElement (!!!!)
514 | return;
515 | }
516 | event.value; // [number, number]
517 | event.target; // HTMLInputElement | HTMLElement (!!!!)
518 | }
519 | ```
520 |
521 | Even though we have narrowed based on `event.value`, the logic doesn't filter up and sideways to `event.target`. This is because a union type `UserTextEvent | UserMouseEvent` could be BOTH at once. So TypeScript needs a better hint. The solution is to use a literal type to tag each case of your union type:
522 |
523 | ```ts
524 | type UserTextEvent = {
525 | type: "TextEvent";
526 | value: string;
527 | target: HTMLInputElement;
528 | };
529 | type UserMouseEvent = {
530 | type: "MouseEvent";
531 | value: [number, number];
532 | target: HTMLElement;
533 | };
534 | type UserEvent = UserTextEvent | UserMouseEvent;
535 | function handle(event: UserEvent) {
536 | if (event.type === "TextEvent") {
537 | event.value; // string
538 | event.target; // HTMLInputElement
539 | return;
540 | }
541 | event.value; // [number, number]
542 | event.target; // HTMLElement
543 | }
544 | ```
545 |
546 | To streamline this you may also combine this with the concept of **User-Defined Type Guards**:
547 |
548 | ```ts
549 | function isString(a: unknown): a is string {
550 | return typeof a === "string";
551 | }
552 | ```
553 |
554 | [Read more about User-Defined Type Guards in the Handbook](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards).
555 |
556 | ## Props: One or the Other but not Both
557 |
558 | Use the `in` keyword, function overloading, and union types to make components that take either one or another sets of props, but not both:
559 |
560 | ```tsx
561 | type Props1 = { foo: string };
562 | type Props2 = { bar: string };
563 |
564 | function MyComponent(props: Props1 | Props2) {
565 | if ("foo" in props) {
566 | // props.bar // error
567 | return
579 | );
580 | ```
581 |
582 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgAUcwBnARjgF44BvOTCBABccFjCjAAdgHM4AXwDcVWvSYRWAJi684AIxRQRYiTPlLK5TAFdJGYBElwAstQDCuSJKSSYACjDMLCJqrBwAPoyBGgCUvBRwcMCYcL4ARAIQqYmOAeossTzxCXAA9CVwuawAdPpQpeVIUDhQRQlEMFZQjgA8ACbAAG4AfDyVLFUZct0l-cPmCXJwSAA2LPSF5MX1FYETgtuNza1w7Z09syNjNQZTM4ND8-IUchRoDmJwAKosKNJI7uAHN4YCJkOgYFUAGKubS+WKcIYpIp9e7HbouAGeYH8QScdKCLIlIZojEeIE+PQGPG1QnEzbFHglABUcHRbjJXgpGTxGSytWpBlSRO2UgGKGWwF6cCZJRe9OmFwo0QUQA)
583 |
584 | Further reading: [how to ban passing `{}` if you have a `NoFields` type.](http://www.javiercasas.com/articles/typescript-impossible-states-irrepresentable)
585 |
586 | ## Props: Must Pass Both
587 |
588 | ```tsx
589 | type OneOrAnother =
590 | | (T1 & { [K in keyof T2]?: undefined })
591 | | (T2 & { [K in keyof T1]?: undefined });
592 |
593 | type Props = OneOrAnother<{ a: string; b: string }, {}>;
594 |
595 | const a: Props = { a: "a" }; // error
596 | const b: Props = { b: "b" }; // error
597 | const ab: Props = { a: "a", b: "b" }; // ok
598 | ```
599 |
600 | Thanks [diegohaz](https://twitter.com/kentcdodds/status/1085655423611367426)
601 |
602 | ## Props: Can Optionally Pass One Only If the Other Is Passed
603 |
604 | Say you want a Text component that gets truncated if `truncate` prop is passed but expands to show the full text when `expanded` prop is passed (e.g. when the user clicks the text).
605 |
606 | You want to allow `expanded` to be passed only if `truncate` is also passed, because there is no use for `expanded` if the text is not truncated.
607 |
608 | You can do this by function overloads:
609 |
610 | ```tsx
611 | type CommonProps = {
612 | children: React.ReactNode;
613 | miscProps?: any;
614 | };
615 |
616 | type NoTruncateProps = CommonProps & { truncate?: false };
617 |
618 | type TruncateProps = CommonProps & { truncate: true; expanded?: boolean };
619 |
620 | // Function overloads to accept both prop types NoTruncateProps & TruncateProps
621 | function Text(props: NoTruncateProps): JSX.Element;
622 | function Text(props: TruncateProps): JSX.Element;
623 | function Text(props: CommonProps & { truncate?: boolean; expanded?: boolean }) {
624 | const { children, truncate, expanded, ...otherProps } = props;
625 | const classNames = truncate ? ".truncate" : "";
626 | return (
627 |
628 | {children}
629 |
630 | );
631 | }
632 | ```
633 |
634 | Using the Text component:
635 |
636 | ```tsx
637 | const App: React.FC = () => (
638 | <>
639 | {/* these all typecheck */}
640 | not truncated
641 | truncated
642 |
643 | truncate-able but expanded
644 |
645 | {/* TS error: Property 'truncate' is missing in type '{ children: string; expanded: true; }' but required in type '{ truncate: true; expanded?: boolean | undefined; }'. */}
646 | truncate-able but expanded
647 | >
648 | );
649 | ```
650 |
651 | ## Omit attribute from a type
652 |
653 | Note: [Omit was added as a first class utility in TS 3.5](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittk)! 🎉
654 |
655 | Sometimes when intersecting types, we want to define our own version of an attribute. For example, I want my component to have a `label`, but the type I am intersecting with also has a `label` attribute. Here's how to extract that out:
656 |
657 | ```tsx
658 | export interface Props {
659 | label: React.ReactNode; // this will conflict with the InputElement's label
660 | }
661 |
662 | // this comes inbuilt with TS 3.5
663 | type Omit = Pick>;
664 |
665 | // usage
666 | export const Checkbox = (
667 | props: Props & Omit, "label">
668 | ) => {
669 | const { label } = props;
670 | return (
671 |
672 |
675 | {label}
676 |
677 | );
678 | };
679 | ```
680 |
681 | When your component defines multiple props, chances of those conflicts increase. However you can explicitly state that all your fields should be removed from the underlying component using the `keyof` operator:
682 |
683 | ```tsx
684 | export interface Props {
685 | label: React.ReactNode; // conflicts with the InputElement's label
686 | onChange: (text: string) => void; // conflicts with InputElement's onChange
687 | }
688 |
689 | export const Textbox = (
690 | props: Props & Omit, keyof Props>
691 | ) => {
692 | // implement Textbox component ...
693 | };
694 | ```
695 |
696 | ## Type Zoo
697 |
698 | As you can see from the Omit example above, you can write significant logic in your types as well. [type-zoo](https://github.com/pelotom/type-zoo) is a nice toolkit of operators you may wish to check out (includes Omit), as well as [utility-types](https://github.com/piotrwitek/utility-types) (especially for those migrating from Flow).
699 |
700 | ## Extracting Prop Types of a Component
701 |
702 | _(Contributed by [@ferdaber](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/63))_
703 |
704 | There are a lot of places where you want to reuse some slices of props because of prop drilling,
705 | so you can either export the props type as part of the module or extract them (either way works).
706 |
707 | The advantage of extracting the prop types is that you won't need to export everything, and a refactor of the source of truth component will propagate to all consuming components.
708 |
709 | ```ts
710 | import { ComponentProps, JSXElementConstructor } from "react";
711 |
712 | // goes one step further and resolves with propTypes and defaultProps properties
713 | type ApparentComponentProps<
714 | C extends keyof JSX.IntrinsicElements | JSXElementConstructor
715 | > = C extends JSXElementConstructor
716 | ? JSX.LibraryManagedAttributes
717 | : ComponentProps;
718 | ```
719 |
720 | You can also use them to strongly type custom event handlers if they're not written at the call sites themselves
721 | (i.e. inlined with the JSX attribute):
722 |
723 | ```tsx
724 | // my-inner-component.tsx
725 | export function MyInnerComponent(props: {
726 | onSomeEvent(
727 | event: ComplexEventObj,
728 | moreArgs: ComplexArgs
729 | ): SomeWeirdReturnType;
730 | }) {
731 | /* ... */
732 | }
733 |
734 | // my-consuming-component.tsx
735 | export function MyConsumingComponent() {
736 | // event and moreArgs are contextually typed along with the return value
737 | const theHandler: Props["onSomeEvent"] = (
738 | event,
739 | moreArgs
740 | ) => {};
741 | return ;
742 | }
743 | ```
744 |
745 | ## Handling Exceptions
746 |
747 | You can provide good information when bad things happen.
748 |
749 | ```ts
750 | class InvalidDateFormatError extends RangeError {}
751 | class DateIsInFutureError extends RangeError {}
752 |
753 | /**
754 | * // optional docblock
755 | * @throws {InvalidDateFormatError} The user entered date incorrectly
756 | * @throws {DateIsInFutureError} The user entered date in future
757 | *
758 | */
759 | function parse(date: string) {
760 | if (!isValid(date))
761 | throw new InvalidDateFormatError("not a valid date format");
762 | if (isInFuture(date)) throw new DateIsInFutureError("date is in the future");
763 | // ...
764 | }
765 |
766 | try {
767 | // call parse(date) somewhere
768 | } catch (e) {
769 | if (e instanceof InvalidDateFormatError) {
770 | console.error("invalid date format", e);
771 | } else if (e instanceof DateIsInFutureError) {
772 | console.warn("date is in future", e);
773 | } else {
774 | throw e;
775 | }
776 | }
777 | ```
778 |
779 | [View in TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BJAOwDcVrgATAERRhIAYtBACAolBxQ4SAB6CW3RghQsA5kknS4AbwC+VWgzj9BTOqyEBXGNaLboshUiUq1mxzIMUKmaywYwBAscMB0AGqcPAAU3AJIAFxwdDBQwBoAlHoUcHBEdlCh8YJwAPxwadZIcMmYnHRIANwUhpTk-oEwwaHhVrb2SHEJyanpWTnkeWghqXAlSAByEADucAC8cCxIa2ZDmS1TcDMsc2j2RCwwextbO6YJw4KZuXCvBfah51Ku1wkAdJoYAAVUD7OAAPnmCWWK0BSBBYJiB1avnIAHoAFSY3KYuDo9FwCBgbohTjzCBoABG1EpAGtcXAAAIwAAWOBWjF0rA4XD4CREUDEMC8+jgwNZNWsjRkvyQRG40NKGRmPww1AAnoyWezVly9hZ+oUtFJoGKJVKZbIrvKkIqFmFQv5jbjcei-AEgiE4GAUFBGk8kik0hl1NldK9gJg4DEAIThKJ8wOZF5HPJsjl3NY86L8wSC4VeGIAIhYEHgKDgvJ4SpqmFEAmLKKOUZjfRYNmNyeyGdWWYe5ksHYGDlNUBLDvCjsqkrgzsGTcOeQJcH+a9R7TSGsmy8JaE41B9foDC2ydFwO0lRFaxwEaFZMaQ4cj0ZiNQyqTUaCQEGjOb5ewFhIY7PmmxyzBA1BIP88rSCWGTVvaCRzg2MDFgANLIzZ5GKSDUI0YSvu+pwwF+P7RgaQ6doMXigXk0wQVB-wrH6LATshU4ZHOI5IBhWFLnAuH4TUEZgb2azNK8bT6EAA)
780 |
781 | Simply throwing an exception is fine, however it would be nice to make TypeScript remind the consumer of your code to handle your exception. We can do that just by returning instead of throwing:
782 |
783 | ```ts
784 | function parse(
785 | date: string
786 | ): Date | InvalidDateFormatError | DateIsInFutureError {
787 | if (!isValid(date))
788 | return new InvalidDateFormatError("not a valid date format");
789 | if (isInFuture(date)) return new DateIsInFutureError("date is in the future");
790 | // ...
791 | }
792 |
793 | // now consumer *has* to handle the errors
794 | let result = parse("mydate");
795 | if (result instanceof InvalidDateFormatError) {
796 | console.error("invalid date format", result.message);
797 | } else if (result instanceof DateIsInFutureError) {
798 | console.warn("date is in future", result.message);
799 | } else {
800 | /// use result safely
801 | }
802 |
803 | // alternately you can just handle all errors
804 | if (result instanceof Error) {
805 | console.error("error", result);
806 | } else {
807 | /// use result safely
808 | }
809 | ```
810 |
811 | You can also describe exceptions with special-purpose data types (don't say monads...) like the `Try`, `Option` (or `Maybe`), and `Either` data types:
812 |
813 | ```ts
814 | interface Option {
815 | flatMap(f: (value: T) => None): None
816 | flatMap(f: (value: T) => Option): Option
817 | getOrElse(value: T): T
818 | }
819 | class Some implements Option {
820 | constructor(private value: T) {}
821 | flatMap(f: (value: T) => None): None
822 | flatMap(f: (value: T) => Some): Some
823 | flatMap(f: (value: T) => Option): Option {
824 | return f(this.value)
825 | }
826 | getOrElse(): T {
827 | return this.value
828 | }
829 | }
830 | class None implements Option {
831 | flatMap(): None {
832 | return this
833 | }
834 | getOrElse(value: U): U {
835 | return value
836 | }
837 | }
838 |
839 | // now you can use it like:
840 | let result = Option(6) // Some
841 | .flatMap(n => Option(n*3)) // Some
842 | .flatMap(n = new None) // None
843 | .getOrElse(7)
844 |
845 | // or:
846 | let result = ask() // Option
847 | .flatMap(parse) // Option
848 | .flatMap(d => new Some(d.toISOString()) // Option
849 | .getOrElse('error parsing string')
850 | ```
851 |
852 | ## Third Party Libraries
853 |
854 | Sometimes DefinitelyTyped can get it wrong, or isn't quite addressing your use case. You can declare your own file with the same interface name. Typescript will merge interfaces with the same name.
855 |
856 | # Section 2: Useful Patterns by TypeScript Version
857 |
858 | TypeScript Versions often introduce new ways to do things; this section helps current users of React + TypeScript upgrade TypeScript versions and explore patterns commonly used by TypeScript + React apps and libraries. This may have duplications with other sections; if you spot any discrepancies, [file an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new)!
859 |
860 | _TypeScript version guides before 2.9 are unwritten, please feel free to send a PR!_ Apart from official TS team communication we also recommend [Marius Schulz's blog for version notes](https://mariusschulz.com/). For more TypeScript history, see [A Brief History of TypeScript Types](https://github.com/blakeembrey/a-brief-history-of-types-with-typescript) and [A Brief History of DefinitelyTyped](https://blog.johnnyreilly.com/2019/10/definitely-typed-movie.html)
861 |
862 | ## TypeScript 2.9
863 |
864 | [[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html) | [Blog Post](https://blogs.msdn.microsoft.com/typescript/2018/05/31/announcing-typescript-2-9/)]
865 |
866 | 1. Type arguments for tagged template strings (e.g. `styled-components`):
867 |
868 | ```tsx
869 | export interface InputFormProps {
870 | foo: string; // this is understood inside the template string below
871 | }
872 |
873 | export const InputForm = styledInput`
874 | color:
875 | ${({ themeName }) => (themeName === "dark" ? "black" : "white")};
876 | border-color: ${({ foo }) => (foo ? "red" : "black")};
877 | `;
878 | ```
879 |
880 | 2. **JSX Generics**
881 |
882 | https://github.com/Microsoft/TypeScript/pull/22415
883 |
884 | Helps with typing/using generic components:
885 |
886 | ```tsx
887 | // instead of
888 | ) => ....}/>
889 |
890 | // usage
891 | render={props => ...}/>
892 | data={12} />
893 | ```
894 |
895 | More info: https://github.com/basarat/typescript-book/blob/master/docs/jsx/react.md#react-jsx-tip-generic-components
896 |
897 | ## TypeScript 3.0
898 |
899 | [[Release Notes](https://github.com/Microsoft/TypeScript/releases/tag/v3.0.1) | [Blog Post](https://blogs.msdn.microsoft.com/typescript/2018/07/30/announcing-typescript-3-0/)]
900 |
901 | 1. Typed rest parameters for writing arguments of variable length:
902 |
903 | ```ts
904 | // `rest` accepts any number of strings - even none!
905 | function foo(...rest: string[]) {
906 | // ...
907 | }
908 |
909 | foo("hello"); // works
910 | foo("hello", "world"); // also works
911 | ```
912 |
913 | 2. Support for `propTypes` and `static defaultProps` in JSX using `LibraryManagedAttributes`:
914 |
915 | ```tsx
916 | export interface Props {
917 | name: string;
918 | }
919 |
920 | export class Greet extends React.Component {
921 | render() {
922 | const { name } = this.props;
923 | return
Hello ${name.toUpperCase()}!
;
924 | }
925 | static defaultProps = { name: "world" };
926 | }
927 |
928 | // Type-checks! No type assertions needed!
929 | let el = ;
930 | ```
931 |
932 | 3. new `Unknown` type
933 |
934 | For typing API's to force type checks - not specifically React related, however very handy for dealing with API responses:
935 |
936 | ```tsx
937 | interface IComment {
938 | date: Date;
939 | message: string;
940 | }
941 |
942 | interface IDataService1 {
943 | getData(): any;
944 | }
945 |
946 | let service1: IDataService1;
947 | const response = service1.getData();
948 | response.a.b.c.d; // RUNTIME ERROR
949 |
950 | // ----- compare with -------
951 |
952 | interface IDataService2 {
953 | getData(): unknown; // ooo
954 | }
955 |
956 | let service2: IDataService2;
957 | const response2 = service2.getData();
958 | // response2.a.b.c.d; // COMPILE TIME ERROR if you do this
959 |
960 | if (typeof response === "string") {
961 | console.log(response.toUpperCase()); // `response` now has type 'string'
962 | }
963 | ```
964 |
965 | You can also assert a type, or use a **type guard** against an `unknown` type. This is better than resorting to `any`.
966 |
967 | 4. Project References
968 |
969 | Project references allow TypeScript projects to depend on other TypeScript projects – specifically, allowing tsconfig.json files to reference other tsconfig.json files. This lets large codebases scale without recompiling every part of the codebase every time, by breaking it up into multiple projects.
970 |
971 | In each folder, create a tsconfig.json that includes at least:
972 |
973 | ```js
974 | {
975 | "compilerOptions": {
976 | "composite": true, // tells TSC it is a subproject of a larger project
977 | "declaration": true, // emit .d.ts declaration files since project references dont have access to source ts files. important for project references to work!
978 | "declarationMap": true, // sourcemaps for .d.ts
979 | "rootDir": "." // specify compile it relative to root project at .
980 | },
981 | "include": [
982 | "./**/*.ts
983 | ],
984 | "references": [ // (optional) array of subprojects your subproject depends on
985 | {
986 | "path": "../myreferencedproject", // must have tsconfig.json
987 | "prepend": true // concatenate js and sourcemaps generated by this subproject, if and only if using outFile
988 | }
989 | ]
990 | }
991 | ```
992 |
993 | and the root `tsconfig.json` that references top level subproject:
994 |
995 | ```js
996 | {
997 | "files: [],
998 | "references": [
999 | {"path": "./proj1"},
1000 | {"path": "./proj2"},
1001 | ]
1002 | }
1003 | ```
1004 |
1005 | and you must run `tsc --build` or `tsc -b`.
1006 |
1007 | To save the tsconfig boilerplate, you can use the `extends` option:
1008 |
1009 | ```js
1010 | {
1011 | "extends": "../tsconfig.base",
1012 | // more stuff here
1013 | }
1014 | ```
1015 |
1016 | ## TypeScript 3.1
1017 |
1018 | [[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-1.html) | [Blog Post](https://blogs.msdn.microsoft.com/typescript/announcing-typescript-3-1/)]
1019 |
1020 | 1. Properties declarations on functions
1021 |
1022 | Attaching properties to functions like this "just works" now:
1023 |
1024 | ```tsx
1025 | export const FooComponent = ({ name }) =>
;
1079 |
1080 | type NestedProps = { foo: number; stuff: T };
1081 |
1082 | declare class GenericComponent extends Component> {}
1083 |
1084 | // type is 'new (props: NestedProps) => Component>'
1085 | const GenericComponent2 = myHoc(GenericComponent);
1086 | ```
1087 |
1088 | See also [Notes from Google upgrading to 3.5](https://github.com/microsoft/TypeScript/issues/33272)
1089 |
1090 | ## TypeScript 3.6
1091 |
1092 | [[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-6/)]
1093 |
1094 | Nothing particularly React specific but [the playground](https://github.com/agentcooper/typescript-play) got an upgrade and [Ambient Classes and Functions Can Merge](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html#ambient-classes-and-functions-can-merge)
1095 |
1096 | ## TypeScript 3.7
1097 |
1098 | [[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/)]
1099 |
1100 | 1. Optional Chaining
1101 |
1102 | ```ts
1103 | let x = foo?.bar.baz();
1104 |
1105 | // is equivalent to
1106 |
1107 | let x = foo === null || foo === undefined ? undefined : foo.bar.baz();
1108 |
1109 | // Optional Element access
1110 | function tryGetFirstElement(arr?: T[]) {
1111 | return arr?.[0];
1112 | }
1113 |
1114 | // Optional Call
1115 | async function makeRequest(url: string, log?: (msg: string) => void) {
1116 | log?.(`Request started at ${new Date().toISOString()}`);
1117 | const result = (await fetch(url)).json();
1118 | log?.(`Request finished at at ${new Date().toISOString()}`);
1119 | return result;
1120 | }
1121 | ```
1122 |
1123 | 2. Nullish Coalescing
1124 |
1125 | ```ts
1126 | let x = foo ?? bar();
1127 |
1128 | // equivalent to
1129 |
1130 | let x = foo !== null && foo !== undefined ? foo : bar();
1131 | ```
1132 |
1133 | **YOU SHOULD USUALLY USE `??` WHEREVER YOU NORMALLY USE `||`** unless you truly mean falsiness:
1134 |
1135 | ```tsx
1136 | function ShowNumber({ value }: { value: number }) {
1137 | let _value = value || 0.5; // will replace 0 with 0.5 even if user really means 0
1138 | // etc...
1139 | }
1140 | ```
1141 |
1142 | 3. Assertion Functions
1143 |
1144 | ```tsx
1145 | function assert(condition: any, msg?: string): asserts condition {
1146 | if (!condition) {
1147 | throw new AssertionError(msg);
1148 | }
1149 | }
1150 | function yell(str) {
1151 | assert(typeof str === "string");
1152 |
1153 | return str.toUppercase();
1154 | // ~~~~~~~~~~~
1155 | // error: Property 'toUppercase' does not exist on type 'string'.
1156 | // Did you mean 'toUpperCase'?
1157 | }
1158 | ```
1159 |
1160 | You can also assert without a custom function:
1161 |
1162 | ```tsx
1163 | function assertIsString(val: any): asserts val is string {
1164 | if (typeof val !== "string") {
1165 | throw new AssertionError("Not a string!");
1166 | }
1167 | }
1168 | function yell(str: any) {
1169 | assertIsString(str);
1170 |
1171 | // Now TypeScript knows that 'str' is a 'string'.
1172 |
1173 | return str.toUppercase();
1174 | // ~~~~~~~~~~~
1175 | // error: Property 'toUppercase' does not exist on type 'string'.
1176 | // Did you mean 'toUpperCase'?
1177 | }
1178 | ```
1179 |
1180 | 4. `ts-nocheck`
1181 |
1182 | You can now add `// @ts-nocheck` to the top of TypeScript files! good for migrations.
1183 |
1184 | ## TypeScript Roadmap and Spec
1185 |
1186 | https://github.com/Microsoft/TypeScript/wiki/Roadmap
1187 |
1188 | Did you also know you can read the TypeScript spec online?? https://github.com/microsoft/TypeScript/blob/master/doc/spec.md
1189 |
1190 | # Section 3: Misc. Concerns
1191 |
1192 | Sometimes writing React isn't just about React. While we don't focus on other libraries like Redux (see below for more on that), here are some tips on other common concerns when making apps with React + TypeScript.
1193 |
1194 | ## Writing TypeScript Libraries instead of Apps
1195 |
1196 | `propTypes` may seem unnecessary with TypeScript, especially when building React + TypeScript **apps**, but they are still relevant when writing **libraries** which may be used by developers working in Javascript.
1197 |
1198 | ```ts
1199 | interface IMyComponentProps {
1200 | autoHeight: boolean;
1201 | secondProp: number;
1202 | }
1203 |
1204 | export class MyComponent extends React.Component {
1205 | static propTypes = {
1206 | autoHeight: PropTypes.bool,
1207 | secondProp: PropTypes.number.isRequired
1208 | };
1209 | }
1210 | ```
1211 |
1212 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new).
1213 |
1214 | ## Commenting Components
1215 |
1216 | Typescript uses [TSDoc](https://github.com/Microsoft/tsdoc), a variant of JSDoc for Typescript. This is very handy for writing component libraries and having useful descriptions pop up in autocomplete and other tooling (like the [Docz PropsTable](https://www.docz.site/documentation/components-api#propstable)). The main thing to remember is to use `/** YOUR_COMMENT_HERE */` syntax in the line just above whatever you're annotating.
1217 |
1218 | ```tsx
1219 | import React from "react";
1220 |
1221 | interface MyProps {
1222 | /** Description of prop "label".
1223 | * @default foobar
1224 | * */
1225 | label?: string;
1226 | }
1227 |
1228 | /**
1229 | * General component description in JSDoc format. Markdown is *supported*.
1230 | */
1231 | export default function MyComponent({ label = "foobar" }: MyProps) {
1232 | return
Hello world {label}
;
1233 | }
1234 | ```
1235 |
1236 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgFkBPABRzAGc4BvCnDgB6AFRi4AESQ80UYGBjAI1OBExww3OACIANigBGSfboB0Q4ZIACAEySMArvqwQIRlFCtxJYkVaGJvoA-ABccDwwCtQA5gDcFAC+FBTiYkKSAOJI1PQo+nBouJB5tHAOcgpKKmo0cABSAMpSEGhwmNAgKDDmrF4A1nYQAO51fGI8TmCQsEh2YpbkvgHkSAAes-AOzq4dTtQYtaxsAMIlqrkwABT8cEGmcAC8ep0eXrpwSRHsXBC8AEoBFYiDAnFA1AAeOzAABuAD4ABKmfQQOAjaD6OwCB76JKQkQwhGJchJIA)
1237 |
1238 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new).
1239 |
1240 | ## Namespaced Components
1241 |
1242 | Often when creating similar components or components that have a parent-child relationship, it is useful to namespace your components. Types can easily be added be using `Object.assign()`;
1243 |
1244 | ```tsx
1245 | import React from "react";
1246 |
1247 | const Input = (props: any) => ;
1248 |
1249 | const Form = React.forwardRef(
1250 | ({ children, ...otherProps }, ref) => (
1251 |
1254 | )
1255 | );
1256 |
1257 | /**
1258 | * Exported components now can be used as ``
1259 | */
1260 | export default Object.assign(Form, { Input: Input });
1261 | ```
1262 |
1263 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2&ssl=1&ssc=1&pln=14&pc=52#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BJGsAV3gF44AKMHMOgC44KGgE8AlHA4A+OAB5gLdnADeAOk18IAgL5wA9DIpVaDOADFoeLsnQx1maAHcUUACbJM8gBIAVAFkAGQARYAA3AFEAGyQQJBoYABoRcRlublU0AAtgaPciGhTNdQgYbKQoAAV+Ol0UokwpWR4KOAUnKDwNTTKK6tr9Ro5VRt1jcnb2rNz8wt02hQNOkAmJCQBuE3IDACpdtt24SIAPSFgkdzhqcFoEmDo4Gghna9E4ACMkOFY6S5FHgADeRWLoyQGpK7A0EgdTMNgwcGHAwUJBnaDwdxITAoVjReAAeQ+ACskBh1Cg6HRgABzGjcGEpVTw9jCFkwXSbIA)
1264 |
1265 | (Contributed by @bryceosterhaus, see [further discussion](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/165))
1266 |
1267 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new).
1268 |
1269 | ## Design System Development
1270 |
1271 | I do like [Docz](https://docz.site/) which takes basically [1 line of config](https://www.docz.site/documentation/project-configuration#typescript) to accept Typescript. However it is newer and has a few more rough edges (many breaking changes since it is still < v1.0)
1272 |
1273 | For developing with Storybook, read the docs I wrote over here: . This includes automatic proptype documentation generation, which is awesome :)
1274 |
1275 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new).
1276 |
1277 | ## Migrating From Flow
1278 |
1279 | You should check out large projects that are migrating from flow to pick up concerns and tips:
1280 |
1281 | - [Jest](https://github.com/facebook/jest/pull/7554)
1282 | - [Expo](https://github.com/expo/expo/issues/2164)
1283 | - [React-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd/issues/982)
1284 | - [Storybook](https://github.com/storybooks/storybook/issues/5030)
1285 | - [VueJS](https://medium.com/the-vue-point/plans-for-the-next-iteration-of-vue-js-777ffea6fabf)
1286 |
1287 | Useful libraries:
1288 |
1289 | -
1290 | -
1291 | -
1292 |
1293 | If you have specific advice in this area, please file a PR!
1294 |
1295 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new).
1296 |
1297 | ## Prettier
1298 |
1299 | There isn't any real secret to Prettier for TypeScript. But its a great idea to run prettier on every commit!
1300 |
1301 | ```js
1302 | yarn add -D prettier husky lint-staged
1303 |
1304 | // inside package.json
1305 | {
1306 | //...
1307 | "husky": {
1308 | "hooks": {
1309 | "pre-commit": "lint-staged"
1310 | }
1311 | },
1312 | "lint-staged": {
1313 | "linters": {
1314 | "src/*.{ts,tsx,js,jsx,css,scss,md}": [
1315 | "prettier --trailing-comma es5 --single-quote --write",
1316 | "git add"
1317 | ],
1318 | "ignore": [
1319 | "**/dist/*, **/node_modules/*"
1320 | ]
1321 | }
1322 | },
1323 | "prettier": {
1324 | "printWidth": 80,
1325 | "semi": false,
1326 | "singleQuote": true,
1327 | "trailingComma": "es5"
1328 | }
1329 | }
1330 | ```
1331 |
1332 | Integrating this with ESlint may be a problem. We haven't written much on this yet, please contribute if you have a strong opinion. [Here's a helpful gist.](https://gist.github.com/JirkaVebr/519c7597517e4ba756d5b89e7cb4cc0e)
1333 |
1334 | For library authors, this is set up for you in [tsdx](https://github.com/palmerhq/tsdx/pull/45/files).
1335 |
1336 | ## Testing
1337 |
1338 | Yes, you can test your types! You shouldn't use it for EVERYTHING, but it can help prevent regressions:
1339 |
1340 | - https://github.com/azz/jest-runner-tsc
1341 | - https://github.com/SamVerschueren/tsd
1342 | - https://github.com/ikatyang/dts-jest ([Demo](https://codesandbox.io/s/dts-test-frozen-public-demo-iyorn))
1343 | - https://github.com/microsoft/dtslint ([Intro to dtslint](https://www.youtube.com/watch?v=nygcFEwOG8w&feature=share))
1344 |
1345 | ## Working with Non-TypeScript Libraries (writing your own index.d.ts)
1346 |
1347 | Lets say you want to use `de-indent`, but it isn't typed or on DefinitelyTyped. You get an error like this:
1348 |
1349 | ```
1350 | [ts]
1351 | Could not find a declaration file for module 'de-indent'. '/Users/swyx/Work/react-sfc-loader/node_modules/de-indent/index.js' implicitly has an 'any' type.
1352 | Try `npm install @types/de-indent` if it exists or add a new declaration (.d.ts) file containing `declare module 'de-indent';` [7016]
1353 | ```
1354 |
1355 | So create a `.d.ts` file anywhere in your project with the module definition:
1356 |
1357 | ```ts
1358 | // de-indent.d.ts
1359 | declare module "de-indent" {
1360 | function deindent(): void;
1361 | export = deindent; // default export
1362 | }
1363 | ```
1364 |
1365 |
1366 |
1367 | Further Discussion
1368 |
1369 | Any other tips? Please contribute on this topic! [We have an ongoing issue here with some references](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/8). We have more discussion and examples [in our issue here](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/12).
1370 |
1371 |
1372 |
1373 | # Section 4: @types/react and @types/react-dom APIs
1374 |
1375 | The `@types` typings export both "public" types meant for your use as well as "private" types that are for internal use.
1376 |
1377 | Check [SaltyCrane's React TypeScript Cheatsheet](https://github.com/saltycrane/typescript-cheatsheet) for a nice autogenerated complete reference.
1378 |
1379 | ## `@types/react`
1380 |
1381 | [Link to `.d.ts`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts)
1382 |
1383 | **Namespace: React**
1384 |
1385 | Most Commonly Used Interfaces and Types
1386 |
1387 | - `ReactNode` - anything that is renderable _inside_ of JSX, this is NOT the same as what can be rendered by a component!
1388 | - `Component` - base class of all class-based components
1389 | - `PureComponent` - base class for all class-based optimized components
1390 | - `FC`, `FunctionComponent` - a complete interface for function components, often used to type external components instead of typing your own
1391 | - `CSSProperties` - used to type style objects
1392 | - all events: used to type event handlers
1393 | - all event handlers: used to type event handlers
1394 | - all consts: `Children`, `Fragment`, ... are all public and reflect the React runtime namespace
1395 |
1396 | Not Commonly Used but Good to know
1397 |
1398 | - `Ref` - used to type `innerRef`
1399 | - `ElementType` - used for higher order components or operations on components
1400 | - `ComponentType` - used for higher order components where you don't specifically deal with the intrinsic components
1401 | - `ReactPortal` - used if you specifically need to type a prop as a portal, otherwise it is part of `ReactNode`
1402 | - `ComponentClass` - a complete interface for the produced constructor function of a class declaration that extends `Component`, often used to type external components instead of typing your own
1403 | - `JSXElementConstructor` - anything that TypeScript considers to be a valid thing that can go into the opening tag of a JSX expression
1404 | - `ComponentProps` - props of a component
1405 | - `ComponentPropsWithRef` - props of a component where if it is a class-based component it will replace the `ref` prop with its own instance type
1406 | - `ComponentPropsWithoutRef` - props of a component without its `ref` prop
1407 | - all methods: `createElement`, `cloneElement`, ... are all public and reflect the React runtime API
1408 |
1409 | [@Ferdaber's note](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/69): I discourage the use of most `...Element` types because of how black-boxy `JSX.Element` is. You should almost always assume that anything produced by `React.createElement` is the base type `React.ReactElement`.
1410 |
1411 | **Namespace: JSX**
1412 |
1413 | - `Element` - the type of any JSX expression
1414 | - `LibraryManagedAttributes` - It specifies other places where JSX elements can declare and initialize property types. Used to resolve static `defaultProps` and `propTypes` with the internal props type of a component.
1415 | - `IntrinsicElements` - every possible built-in component that can be typed in as a lowercase tag name in JSX
1416 |
1417 | Not commonly used but good to know
1418 |
1419 | - `IntrinsicAttributes` set of attributes that all `IntrinsicElements` support... basically just `key`.
1420 | - `ElementChildrenAttribute` name of property that TS looks at to figure out what types of children a component supports. Basically the `children` property
1421 | - `ElementAttributesProperty` name of property that TS looks at to figure out what attributes a component supports. Basically the `props` property (for a class instance)
1422 |
1423 | **Don't use/Internal/Deprecated**
1424 |
1425 | Anything not listed above is considered an internal type and not public. If you're not sure you can check out the source of `@types/react`. The types are annotated accordingly.
1426 |
1427 | - `SFCElement`
1428 | - `SFC`
1429 | - `ComponentState`
1430 | - `LegacyRef`
1431 | - `StatelessComponent`
1432 | - `ReactType`
1433 |
1434 | ### Adding non-standard attributes
1435 |
1436 | The attributes allowed on host components such as `button` or `img` follow the
1437 | HTML living standard. New features that are not yet part of specification
1438 | or are only implemented by certain browsers will therefore cause a type error. If
1439 | you specifically write code for these browsers or polyfill this attributes you can
1440 | use [module augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation) to still get those components type checked without having
1441 | to use `any` or `@ts-ignore`.
1442 |
1443 | In this example we'll add the [`loading`](https://www.chromestatus.com/feature/5645767347798016) attribute which adds support for [lazy-loading](https://web.dev/native-lazy-loading) images on Chrome:
1444 |
1445 | ```ts
1446 | // react-unstable-attributes.d.ts
1447 | import "react";
1448 |
1449 | declare module "react" {
1450 | interface ImgHTMLAttributes extends HTMLAttributes {
1451 | loading?: "auto" | "eager" | "lazy";
1452 | }
1453 | }
1454 | ```
1455 |
1456 | ## `@types/react-dom`
1457 |
1458 | To be written
1459 |
1460 | # My question isn't answered here!
1461 |
1462 | - [File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new).
1463 |
--------------------------------------------------------------------------------
/AVANZADO.md:
--------------------------------------------------------------------------------
1 |
25 |
26 | ---
27 |
28 | # Cheatsheet Avanzado
29 |
30 | **Esta Cheatsheet Avanzada** ayuda a mostrar y explicar el uso avanzado de tipos genéricos para personas que escriben utilidades/funciones de tipo reutilizable/props de render/componentes de orden superior y **bibliotecas** TS+React.
31 |
32 | - También tiene varios consejos y trucos para usuarios profesionales.
33 | - Consejos para contribuir a DefinitelyTyped
34 | - El objetivo es _aprovechar al máximo_ TypeScript.
35 |
36 | **Creación de bibliotecas React + TypeScript**
37 |
38 | La mejor herramienta para crear bibliotecas React + TS en este momento es [`tsdx`](https://github.com/palmerhq/tsdx). Ejecute `npx tsdx create` y seleccione la opción "react". Puede ver la [Guía del usuario de React](https://github.com/palmerhq/tsdx/issues/5) para obtener algunos consejos sobre las mejores prácticas y optimizaciones de la biblioteca React + TS para la producción.
39 |
40 | - Asegúrese de consultar también la [guía de `basarat`'s](https://basarat.gitbooks.io/typescript/content/docs/quick/library.html) para la configuración de la biblioteca tsconfig.
41 | - Desde el mundo angular, consulte https://github.com/bitjson/typescript-starter
42 |
43 | ---
44 |
45 | ### Tabla de contenido para Cheatsheet Avanzado
46 |
47 |
48 |
49 | Expandir tabla de contenido
50 |
51 | - [Sección 0: Tipos de utilidad](#Sección-0-tipos-de-utilidad)
52 | - [Sección 1: Componentes reutilizables / Utilidades de tipos](#Sección-1-Componentes-reutilizablesutilidades-de-tipos)
53 | - [Componentes de orden superior](#de-orden-superior-componentes-hocs)
54 | - [Render Props](#render-props)
55 | - [Componentes de Representación Condicional](#componentes-de-representación-condicional)
56 | - [`as` props (pasando un componente para ser renderizado](#as-props-pasando-un-componente-para-ser-renderizado)
57 | - [Componentes genéricos](#componentes-genéricos)
58 | - [Escribiendo un componente que acepta diferentes accesorios](#escribiendo-un-componente-que-acepta-diferentes-accesorios)
59 | - [Props: uno u otro pero no ambos](#props-uno-u-otro-pero-no-ambos)
60 | - [Props: debe pasar ambos](#props-debe-pasar-ambos)
61 | - [Props: opcionalmente, puedes pasar uno solo si se pasa el otro](#props-opcionalmente-puedes-pasar-uno-solo-si-se-pasa-el-otro)
62 | - [Omitir atributo de un tipo](#omitir-atributo-de-un-tipo)
63 | - [Type Zoo](#type-zoo)
64 | - [Extracción de tipos de Prop de un componente](#extracción-de-tipos-de-prop-de-un-componente)
65 | - [Manejo de excepciones](#manejo-de-excepciones)
66 | - [Bibliotecas de Terceros](#bibliotecas-de-terceros)
67 | - [Sección 2: Patrones útiles por versión de TypeScript](sección-2-patrones-útiles-por-versión-de-typeScript)
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 | - [Sección 3: Misceláneas](#sección-3-Misceláneas)
78 | - [Escribir bibliotecas de TypeScript en lugar de aplicaciones](#escribir-bibliotecas-de-typeScript-en-lugar-de-aplicaciones)
79 | - [Componentes Comentados](#componentes-comentados)
80 | - [Componentes Namespaced](#componentes-namespaced)
81 | - [Desarrollo de Sistemas de Diseño](#desarrollo-de-sistemas-de-diseño)
82 | - [Migrando desde Flow](#migrando-desde-flow)
83 | - [Prettier](#prettier)
84 | - [Testing](#testing)
85 | - [Linting](#linting)
86 | - [Trabajar con bibliotecas que no son de TypeScript (escribe tu propio index.d.ts)](#trabajar-con-bibliotecas-que-no-son-de-typeScript-escribe-tu-propio-indexdts)
87 | - [Sección 4: @types/react y @types/react-dom APIs](#sección-4-typesreact-y-typesreact-dom-apis)
88 | - [Agregando atributos no estandarizados](#agregando-atributos-no-estandarizados)
89 | - [@types/react-dom](#types-react-dom)
90 |
91 |
92 | # Sección 0: Tipos de utilidad
93 |
94 | Asumiremos el conocimiento de los tipos de utilidad cubiertos en la guía de [`typescript-utilities-guide`](https://github.com/typescript-cheatsheets/typescript-utilities-guide) del proyecto hermano. Busque bibliotecas incluidas allí también para sus necesidades de escritura.
95 |
96 | # Sección 1: Guías Avanzadas
97 |
98 | ## Componentes de orden superior
99 |
100 | **Ahora hay una hoja de [Cheatsheet de HOC](./HOC.md) dedicada que puede consultar para obtener algo de práctica sobre HOC.**
101 |
102 | ## Render Props
103 |
104 | A veces querrás escribir una función que pueda tomar un elemento React o una cadena o algo más como Prop. El mejor tipo para usar en tal situación es `React.ReactNode` que se adapta a cualquier lugar normal, bueno, React Node encajaría:
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 | Si está utilizando una función como render prop con `props.children`:
122 |
123 | ```tsx
124 | export interface Props {
125 | children: (foo: string) => React.ReactNode;
126 | }
127 | ```
128 |
129 | [Algo para agregar? Presentar un problema](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/issues/new).
130 |
131 | ## Componentes de Representación Condicional
132 |
133 | Usar [type guards](https://basarat.gitbooks.io/typescript/docs/types/typeGuard.html#user-defined-type-guards)!
134 |
135 | ```tsx
136 | // Props de botón
137 | type ButtonProps = React.ButtonHTMLAttributes & {
138 | href?: undefined;
139 | };
140 |
141 | // Anchor props
142 | type AnchorProps = React.AnchorHTMLAttributes & {
143 | href?: string;
144 | };
145 |
146 | // Opciones de entrada / salida
147 | type Overload = {
148 | (props: ButtonProps): JSX.Element;
149 | (props: AnchorProps): JSX.Element;
150 | };
151 |
152 | // Guard para verificar si existe href en props
153 | const hasHref = (props: ButtonProps | AnchorProps): props is AnchorProps =>
154 | "href" in props;
155 |
156 | // Componente
157 | const Button: Overload = (props: ButtonProps | AnchorProps) => {
158 | // renderizado de anchor
159 | if (hasHref(props)) return ;
160 | // renderizado de botón
161 | return ;
162 | };
163 |
164 | // Uso
165 | function App() {
166 | return (
167 | <>
168 | {/* 😎 Todo bien */}
169 |
170 | Test
171 |
172 | {/* 😭 Error, `disabled` no existe en el elemento de anlaze */}
173 |
174 | Test
175 |
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 |
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 |
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 |
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 ;
420 | }
421 | }
422 | ```
423 |
424 | Ni siquiera necesitan ser props completamente diferentes, siempre que tengan al menos una diferencia en las propiedades:
425 |
426 | ```tsx
427 | type LinkProps = Omit & { to?: string };
428 |
429 | function RouterLink(props: LinkProps | AnchorProps) {
430 | if ("to" in props) {
431 | return ;
432 | } else {
433 | return ;
434 | }
435 | }
436 | ```
437 |
438 |
439 | Enfoque: Componentes Genéricos
440 |
441 | Aquí hay una solución de ejemplo, vea la discusión adicional para otras soluciones. _gracias a [@jpavon](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/12#issuecomment-394440577)_
442 |
443 | ```tsx
444 | interface LinkProps {}
445 | type AnchorProps = React.AnchorHTMLAttributes;
446 | type RouterLinkProps = Omit;
447 |
448 | const Link = (
449 | props: LinkProps & T extends RouterLinkProps ? RouterLinkProps : AnchorProps
450 | ) => {
451 | if ((props as RouterLinkProps).to) {
452 | return ;
453 | } else {
454 | return ;
455 | }
456 | };
457 |
458 | to="/">Mi enlace; // bien
459 | href="/">Mi enlace; // bien
460 | to="/" href="/">
461 | Mi enlace
462 | ; // error
463 | ```
464 |
465 |
466 |
467 |
468 | Enfoque: Composición
469 |
470 | Si desea renderizar condicionalmente un componente, a veces es mejor usar [React's composition model](https://reactjs.org/docs/composition-vs-inheritance.html) para tener componentes más simples y comprender mejor los _tipos_.
471 |
472 | ```tsx
473 | type AnchorProps = React.AnchorHTMLAttributes
474 | type RouterLinkProps = Omit
475 |
476 | interface Button {
477 | as: React.ComponentClass | 'a'
478 | }
479 |
480 | const Button: React.FunctionComponent = (props) => {
481 | const {as: Component, children, ...rest} = props
482 | return (
483 | {children}
484 | )
485 | }
486 |
487 | const AnchorButton: React.FunctionComponent = (props) => (
488 |
489 | )
490 |
491 | const LinkButton: React.FunctionComponent = (props) => (
492 |
493 | )
494 |
495 | Iniciar sesión
496 | Iniciar sesión
497 | Iniciar sesión // Error: la propiedad 'a' no existe en el tipo...
498 | ```
499 |
500 |
501 |
502 | También es posible que desee utilizar Uniones discriminadas, consulte [Expressive React Component APIs with Discriminated Unions](https://blog.andrewbran.ch/expressive-react-component-apis-with-discriminated-unions/).
503 |
504 | Aquí hay una breve intuición para **Tipos de Unión Discriminada**:
505 |
506 | ```ts
507 | type UserTextEvent = { value: string; target: HTMLInputElement };
508 | type UserMouseEvent = { value: [number, number]; target: HTMLElement };
509 | type UserEvent = UserTextEvent | UserMouseEvent;
510 | function handle(event: UserEvent) {
511 | if (typeof event.value === "string") {
512 | event.value; // string
513 | event.target; // HTMLInputElement | HTMLElement (!!!!)
514 | return;
515 | }
516 | event.value; // [number, number]
517 | event.target; // HTMLInputElement | HTMLElement (!!!!)
518 | }
519 | ```
520 |
521 | Even though we have narrowed based on `event.value`, the logic doesn't filter up and sideways to `event.target`. This is because a union type `UserTextEvent | UserMouseEvent` could be BOTH at once. So TypeScript needs a better hint. The solution is to use a literal type to tag each case of your union type:
522 |
523 | ```ts
524 | type UserTextEvent = {
525 | type: "TextEvent";
526 | value: string;
527 | target: HTMLInputElement;
528 | };
529 | type UserMouseEvent = {
530 | type: "MouseEvent";
531 | value: [number, number];
532 | target: HTMLElement;
533 | };
534 | type UserEvent = UserTextEvent | UserMouseEvent;
535 | function handle(event: UserEvent) {
536 | if (event.type === "TextEvent") {
537 | event.value; // string
538 | event.target; // HTMLInputElement
539 | return;
540 | }
541 | event.value; // [number, number]
542 | event.target; // HTMLElement
543 | }
544 | ```
545 |
546 | Para simplificar esto, también puede combinar esto con el concepto de _**User-Defined Type Guards**_.
547 |
548 | ```ts
549 | function isString(a: unknown): a is string {
550 | return typeof a === "string";
551 | }
552 | ```
553 |
554 | [Lea más sobre User-Defined Type Guards en esta guia](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards).
555 |
556 | ## Props: uno u otro pero no ambos
557 |
558 | Use la palabra clave `in`, la sobrecarga de funciones y los tipos de unión para crear componentes que tengan uno u otro conjunto de props pero no ambos:
559 |
560 | ```tsx
561 | type Props1 = { foo: string };
562 | type Props2 = { bar: string };
563 |
564 | function MyComponent(props: Props1 | Props2) {
565 | if ("foo" in props) {
566 | // props.bar // error
567 | return
579 | );
580 | ```
581 |
582 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgAUcwBnARjgF44BvOTCBABccFjCjAAdgHM4AXwDcVWvSYRWAJi684AIxRQRYiTPlLK5TAFdJGYBElwAstQDCuSJKSSYACjDMLCJqrBwAPoyBGgCUvBRwcMCYcL4ARAIQqYmOAeossTzxCXAA9CVwuawAdPpQpeVIUDhQRQlEMFZQjgA8ACbAAG4AfDyVLFUZct0l-cPmCXJwSAA2LPSF5MX1FYETgtuNza1w7Z09syNjNQZTM4ND8-IUchRoDmJwAKosKNJI7uAHN4YCJkOgYFUAGKubS+WKcIYpIp9e7HbouAGeYH8QScdKCLIlIZojEeIE+PQGPG1QnEzbFHglABUcHRbjJXgpGTxGSytWpBlSRO2UgGKGWwF6cCZJRe9OmFwo0QUQA)
583 |
584 | Lectura adicional: [cómo prohibir pasar `{}` si tiene un tipo `NoFields`](http://www.javiercasas.com/articles/typescript-impossible-states-irrepresentable)
585 |
586 | ## Props: debe pasar ambos
587 |
588 | ```tsx
589 | type OneOrAnother =
590 | | (T1 & { [K in keyof T2]?: undefined })
591 | | (T2 & { [K in keyof T1]?: undefined });
592 |
593 | type Props = OneOrAnother<{ a: string; b: string }, {}>;
594 |
595 | const a: Props = { a: "a" }; // error
596 | const b: Props = { b: "b" }; // error
597 | const ab: Props = { a: "a", b: "b" }; // ok
598 | ```
599 |
600 | Gracias [diegohaz](https://twitter.com/kentcdodds/status/1085655423611367426)
601 |
602 | ## Props: opcionalmente, puedes pasar uno solo si se pasa el otro
603 |
604 | Supongamos que desea un componente de texto que se acorta si se pasa el accesorio `truncate` pero se expande para mostrar el texto completo cuando se pasa el accesorio`expanded` (por ejemplo, cuando el usuario hace clic en el texto).
605 |
606 | Desea permitir que se pase `expanded` solo si también se pasa `truncate`, porque no hay uso para `expanded` si el texto no esta acortado.
607 |
608 | Puede hacerlo mediante function overloads:
609 |
610 | ```tsx
611 | type CommonProps = {
612 | children: React.ReactNode;
613 | miscProps?: any;
614 | };
615 |
616 | type NoTruncateProps = CommonProps & { truncate?: false };
617 |
618 | type TruncateProps = CommonProps & { truncate: true; expanded?: boolean };
619 |
620 | // Function overloads acepta ambos tipos de props NoTruncateProps y TruncateProps
621 | function Text(props: NoTruncateProps): JSX.Element;
622 | function Text(props: TruncateProps): JSX.Element;
623 | function Text(props: CommonProps & { truncate?: boolean; expanded?: boolean }) {
624 | const { children, truncate, expanded, ...otherProps } = props;
625 | const classNames = truncate ? ".truncate" : "";
626 | return (
627 |
628 | {children}
629 |
630 | );
631 | }
632 | ```
633 |
634 | Usando el componente de texto:
635 |
636 | ```tsx
637 | const App: React.FC = () => (
638 | <>
639 | {/* todos estos typecheck */}
640 | not truncated
641 | truncated
642 |
643 | truncate-able but expanded
644 |
645 | {/* TS error: Property 'truncate' is missing in type '{ children: string; expanded: true; }' but required in type '{ truncate: true; expanded?: boolean | undefined; }'. */}
646 | truncate-able but expanded
647 | >
648 | );
649 | ```
650 |
651 | ## Omitir atributo de un tipo
652 |
653 | Nota: [Omitir se agregó como una utilidad de primera clase en TS 3.5](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittk)! 🎉
654 |
655 | A veces, cuando se cruzan tipos, queremos definir nuestra propia versión de un atributo. Por ejemplo, quiero que mi componente tenga una `etiqueta`, pero el tipo con el que me estoy cruzando también tiene un atributo `etiqueta`. Aquí se explica cómo extraer ese tipo:
656 |
657 | ```tsx
658 | export interface Props {
659 | label: React.ReactNode; // esto entrará en conflicto con la etiqueta InputElement
660 | }
661 |
662 | // esto viene incorporado con TS 3.5
663 | type Omit = Pick>;
664 |
665 | // uso
666 | export const Checkbox = (
667 | props: Props & Omit, "label">
668 | ) => {
669 | const { label } = props;
670 | return (
671 |
672 |
675 | {label}
676 |
677 | );
678 | };
679 | ```
680 |
681 | Cuando tucomponente define múltiples accesorios, aumentan las posibilidades de esos conflictos. Sin embargo, puede indicar explícitamente que todos sus campos deben eliminarse del componente subyacente utilizando el operador `keyof`:
682 |
683 | ```tsx
684 | export interface Props {
685 | label: React.ReactNode; // entra en conflicto con la etiqueta de InputElement
686 | onChange: (text: string) => void; // entra en conflicto con onChange de InputElement
687 | }
688 |
689 | export const Textbox = (
690 | props: Props & Omit, keyof Props>
691 | ) => {
692 | // implementar el componente Textbox ...
693 | };
694 | ```
695 |
696 | ## Type Zoo
697 |
698 | Como puede ver en el ejemplo anterior de Omitir, también puede escribir una lógica significativa en sus tipos. [type-zoo](https://github.com/pelotom/type-zoo) es un buen conjunto de herramientas de operadores que puede desear consultar (incluye Omitir), así como [tipos de utilidad](https://github.com/piotrwitek/utility-types)(especialmente para aquellos que migran desde Flow).
699 |
700 | ## Extracción de tipos de Prop de un componente
701 |
702 | _(Contribuido por [@ferdaber](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/63))_
703 |
704 | Hay muchos lugares donde desea reutilizar algunas piesas de props debido a _props drilling_,
705 | para que pueda exportar el tipo de accesorios como parte del módulo o extraerlos (de cualquier manera funciona).
706 |
707 | La ventaja de extraer los tipos de props es que no necesitas exportar todo. Y el componente fuente se propagará a todos los componentes consumidores.
708 |
709 | ```ts
710 | import { ComponentProps, JSXElementConstructor } from "react";
711 |
712 | // va un paso más allá y se resuelve con las propiedades propTypes y defaultProps
713 | type ApparentComponentProps<
714 | C extends keyof JSX.IntrinsicElements | JSXElementConstructor
715 | > = C extends JSXElementConstructor
716 | ? JSX.LibraryManagedAttributes
717 | : ComponentProps;
718 | ```
719 |
720 | También puede usarlos para escribir controladores de eventos personalizados si no están escritos en los propios sitios de llamadas
721 | (es decir, en línea con el atributo JSX):
722 |
723 | ```tsx
724 | // my-inner-component.tsx
725 | export function MyInnerComponent(props: {
726 | onSomeEvent(
727 | event: ComplexEventObj,
728 | moreArgs: ComplexArgs
729 | ): SomeWeirdReturnType;
730 | }) {
731 | /* ... */
732 | }
733 |
734 | // my-consuming-component.tsx
735 | export function MyConsumingComponent() {
736 | // event y moreArgs se escriben contextualmente junto con el valor de retorno
737 | const theHandler: Props["onSomeEvent"] = (
738 | event,
739 | moreArgs
740 | ) => {};
741 | return ;
742 | }
743 | ```
744 |
745 | ## Manejo de Excepciones
746 |
747 | Puede proporcionar buena información cuando suceden cosas malas.
748 |
749 | ```ts
750 | class InvalidDateFormatError extends RangeError {}
751 | class DateIsInFutureError extends RangeError {}
752 |
753 | /**
754 | * // opcional docblock
755 | * @throws {InvalidDateFormatError} El usuario ingresó la fecha incorrectamente
756 | * @throws {DateIsInFutureError} El usuario ingresó la fecha en el futuro
757 | *
758 | */
759 | function parse(date: string) {
760 | if (!isValid(date))
761 | throw new InvalidDateFormatError("no es un formato de fecha válido");
762 | if (isInFuture(date))
763 | throw new DateIsInFutureError("la fecha es en el futuro");
764 | // ...
765 | }
766 |
767 | try {
768 | // llamar a parse(date)) en alguna parte
769 | } catch (e) {
770 | if (e instanceof InvalidDateFormatError) {
771 | console.error("formato de fecha inválido", e);
772 | } else if (e instanceof DateIsInFutureError) {
773 | console.warn("la fecha es en el futuro", e);
774 | } else {
775 | throw e;
776 | }
777 | }
778 | ```
779 |
780 | [View in TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BJAOwDcVrgATAERRhIAYtBACAolBxQ4SAB6CW3RghQsA5kknS4AbwC+VWgzj9BTOqyEBXGNaLboshUiUq1mxzIMUKmaywYwBAscMB0AGqcPAAU3AJIAFxwdDBQwBoAlHoUcHBEdlCh8YJwAPxwadZIcMmYnHRIANwUhpTk-oEwwaHhVrb2SHEJyanpWTnkeWghqXAlSAByEADucAC8cCxIa2ZDmS1TcDMsc2j2RCwwextbO6YJw4KZuXCvBfah51Ku1wkAdJoYAAVUD7OAAPnmCWWK0BSBBYJiB1avnIAHoAFSY3KYuDo9FwCBgbohTjzCBoABG1EpAGtcXAAAIwAAWOBWjF0rA4XD4CREUDEMC8+jgwNZNWsjRkvyQRG40NKGRmPww1AAnoyWezVly9hZ+oUtFJoGKJVKZbIrvKkIqFmFQv5jbjcei-AEgiE4GAUFBGk8kik0hl1NldK9gJg4DEAIThKJ8wOZF5HPJsjl3NY86L8wSC4VeGIAIhYEHgKDgvJ4SpqmFEAmLKKOUZjfRYNmNyeyGdWWYe5ksHYGDlNUBLDvCjsqkrgzsGTcOeQJcH+a9R7TSGsmy8JaE41B9foDC2ydFwO0lRFaxwEaFZMaQ4cj0ZiNQyqTUaCQEGjOb5ewFhIY7PmmxyzBA1BIP88rSCWGTVvaCRzg2MDFgANLIzZ5GKSDUI0YSvu+pwwF+P7RgaQ6doMXigXk0wQVB-wrH6LATshU4ZHOI5IBhWFLnAuH4TUEZgb2azNK8bT6EAA)
781 |
782 | Simplemente lanzar una excepción está bien, sin embargo, sería bueno hacer que TypeScript recuerde al consumidor de tu código para manejar tu excepción. Podemos hacerlo simplemente volviendo en lugar de lanzar:
783 |
784 | ```ts
785 | function parse(
786 | date: string
787 | ): Date | InvalidDateFormatError | DateIsInFutureError {
788 | if (!isValid(date))
789 | return new InvalidDateFormatError("no es un formato de fecha válido");
790 | if (isInFuture(date))
791 | return new DateIsInFutureError("la fecha es en el futuro");
792 | // ...
793 | }
794 |
795 | // ahora el consumidor * tiene * para manejar los errores
796 | let result = parse("mydate");
797 | if (result instanceof InvalidDateFormatError) {
798 | console.error("invalid date format", result.message);
799 | } else if (result instanceof DateIsInFutureError) {
800 | console.warn("date is in future", result.message);
801 | } else {
802 | /// usa el resultado de forma segura
803 | }
804 |
805 | // alternativamente, puede manejar todos los errores
806 | if (result instanceof Error) {
807 | console.error("error", result);
808 | } else {
809 | /// usa el resultado de forma segura
810 | }
811 | ```
812 |
813 | También puede describir excepciones con tipos de datos de propósito especial (no diga mónadas ...) como los tipos de datos `Try`,`Option` (o `Maybe`) y`Either`:
814 |
815 | ```ts
816 | interface Option {
817 | flatMap(f: (value: T) => None): None
818 | flatMap(f: (value: T) => Option): Option
819 | getOrElse(value: T): T
820 | }
821 | class Some implements Option {
822 | constructor(private value: T) {}
823 | flatMap(f: (value: T) => None): None
824 | flatMap(f: (value: T) => Some): Some
825 | flatMap(f: (value: T) => Option): Option {
826 | return f(this.value)
827 | }
828 | getOrElse(): T {
829 | return this.value
830 | }
831 | }
832 | class None implements Option {
833 | flatMap(): None {
834 | return this
835 | }
836 | getOrElse(value: U): U {
837 | return value
838 | }
839 | }
840 |
841 | // ahora puedes usarlo como:
842 | let result = Option(6) // Algunos
843 | .flatMap(n => Option(n*3)) // Algunos
844 | .flatMap(n = new None) // Nada
845 | .getOrElse(7)
846 |
847 | // or:
848 | let result = ask() // Opción
849 | .flatMap(parse) // Option
850 | .flatMap(d => new Some(d.toISOString()) // Opción
851 | .getOrElse('cadena de análisis de error')
852 | ```
853 |
854 | ## Bibliotecas de Terceros
855 |
856 | A veces, DefinitelyTyped se puede equivocar o no puede abordar tucaso de uso. Puedes declarar tu propio archivo con el mismo nombre de interfaz. Typecript fusionará interfaces con el mismo nombre.
857 |
858 | # Sección 2: Patrones útiles por versión de TypeScript
859 |
860 | Las versiones de TypeScript a menudo introducen nuevas formas de hacer cosas; Esta sección ayuda a los usuarios actuales de React + TypeScript a actualizar las versiones de TypeScript y explorar los patrones comúnmente utilizados por las aplicaciones y bibliotecas TypeScript + React. Esto puede tener duplicaciones con otras secciones; si detecta alguna error, [abre un issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es/issues/new).
861 |
862 | _Las guías de versiones de TypeScript anteriores a 2.9 no están escritas, ¡no dudes en enviar un PR!_ Además de la comunicación oficial del equipo de TS, también recomendamos [el blog de Marius Schulz para las notas de la versión](https://mariusschulz.com/).
863 |
864 | ## TypeScript 2.9
865 |
866 | [[Notas de la versión](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html) | [Publicación del blog](https://blogs.msdn.microsoft.com/typescript/2018/05/31/announcing-typescript-2-9/)]
867 |
868 | 1. Escriba argumentos para _tagged template strings_ (por ejemplo, `styled-components`):
869 |
870 | ```tsx
871 | export interface InputFormProps {
872 | foo: string; // esto se entiende dentro de la cadena de plantilla a continuación
873 | }
874 |
875 | export const InputForm = styledInput`
876 | color:
877 | ${({ themeName }) => (themeName === "dark" ? "black" : "white")};
878 | border-color: ${({ foo }) => (foo ? "red" : "black")};
879 | `;
880 | ```
881 |
882 | 2. **JSX Generics**
883 |
884 | https://github.com/Microsoft/TypeScript/pull/22415
885 |
886 | Ayuda a escribir / usar componentes genéricos:
887 |
888 | ```tsx
889 | // en lugar de
890 | ) => ....}/>
891 |
892 | // uso
893 | render={props => ...}/>
894 | data={12} />
895 | ```
896 |
897 | Más información: https://github.com/basarat/typescript-book/blob/master/docs/jsx/react.md#react-jsx-tip-generic-components
898 |
899 | ## TypeScript 3.0
900 |
901 | [[Notas de la versión](https://github.com/Microsoft/TypeScript/releases/tag/v3.0.1) | [Publicación del blog](https://blogs.msdn.microsoft.com/typescript/2018/07/30/announcing-typescript-3-0/)]
902 |
903 | 1. Parametros rest con tipo para escribir argumentos de longitud variable:
904 |
905 | ```ts
906 | // `rest` acepta cualquier numero de strings - incluso ninguno!
907 | function foo(...rest: string[]) {
908 | // ...
909 | }
910 |
911 | foo("hello"); // funciona
912 | foo("hello", "world"); // también funciona
913 | ```
914 |
915 | 2. Soporte para `propTypes` y`static defaultProps` en JSX usando `LibraryManagedAttributes`:
916 |
917 | ```tsx
918 | export interface Props {
919 | name: string;
920 | }
921 |
922 | export class Greet extends React.Component {
923 | render() {
924 | const { name } = this.props;
925 | return
Hola ${name.toUpperCase()}!
;
926 | }
927 | static defaultProps = { name: "mundo" };
928 | }
929 |
930 | // ¡Verificaciones de tipo! ¡No se necesitan aserciones de tipo!
931 | let el = ;
932 | ```
933 |
934 | 3. new `Unknown` type
935 |
936 | Para escribir API para forzar verificaciones de tipo, no específicamente relacionadas con React, pero muy útiles para tratar con respuestas de API:
937 |
938 | ```tsx
939 | interface IComment {
940 | date: Date;
941 | message: string;
942 | }
943 |
944 | interface IDataService1 {
945 | getData(): any;
946 | }
947 |
948 | let service1: IDataService1;
949 | const response = service1.getData();
950 | response.a.b.c.d; // ERROR DE TIEMPO DE EJECUCIÓN
951 |
952 | // ----- comparar con -------
953 |
954 | interface IDataService2 {
955 | getData(): unknown; // ooo
956 | }
957 |
958 | let service2: IDataService2;
959 | const response2 = service2.getData();
960 | // response2.a.b.c.d; // COMPILE TIME ERROR if you do this
961 |
962 | if (typeof response === "string") {
963 | console.log(response.toUpperCase()); // `response` ahora tiene el tipo 'string'
964 | }
965 | ```
966 |
967 | También puede afirmar un tipo, o utilizar un **protector de tipo** contra un tipo `desconocido`. Esto es mejor que recurrir a `any`.
968 |
969 | 4. Referencias de proyectos
970 |
971 | Las referencias de proyecto permiten que los proyectos TypeScript dependan de otros proyectos TypeScript, específicamente, permitiendo que los archivos tsconfig.json hagan referencia a otros archivos tsconfig.json. Esto permite que las bases de código grandes escalen sin recompilar cada parte de la base de código cada vez, dividiéndola en múltiples proyectos.
972 |
973 | En cada carpeta, cree un tsconfig.json que incluya al menos:
974 |
975 | ```js
976 | {
977 | "compilerOptions": {
978 | "composite": true, // le dice a TSC que es un subproyecto de un proyecto más grande
979 | "declaration": true, // archivos de declaración emmet .d.ts ya que las referencias de proyecto no tienen acceso a los archivos ts de origen. Importante para que referencias de proyectos pueda trabajar!
980 | "declarationMap": true, // mapas de origen para .d.ts
981 | "rootDir": "." // especifique compilarlo en relación con el proyecto raíz en.
982 | },
983 | "include": [
984 | "./**/*.ts
985 | ],
986 | "references": [ // (opcional) matriz de subproyectos de los que depende tusubproyecto
987 | {
988 | "path": "../myreferencedproject", // debe tener tsconfig.json
989 | "prepend": true // concatenar js y mapas fuente generados por este subproyecto, si y solo si se usa outFile
990 | }
991 | ]
992 | }
993 | ```
994 |
995 | y la raíz `tsconfig.json` que hace referencia al subproyecto de nivel superior:
996 |
997 | ```js
998 | {
999 | "files: [],
1000 | "references": [
1001 | {"path": "./proj1"},
1002 | {"path": "./proj2"},
1003 | ]
1004 | }
1005 | ```
1006 |
1007 | y debe ejecutar `tsc --build` o `tsc -b`.
1008 |
1009 | Para guardar la repetitiva tsconfig, puede usar la opción `extend`:
1010 |
1011 | ```js
1012 | {
1013 | "extends": "../tsconfig.base",
1014 | // otras cosas
1015 | }
1016 | ```
1017 |
1018 | ## TypeScript 3.1
1019 |
1020 | [[Notas de la versión](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-1.html) | [Publicación del blog](https://blogs.msdn.microsoft.com/typescript/announcing-typescript-3-1/)]
1021 |
1022 | 1. Declaraciones de propiedades sobre funciones
1023 |
1024 | Adjuntar propiedades a funciones como esta "simplemente funciona" ahora:
1025 |
1026 | ```tsx
1027 | export const FooComponent = ({ name }) =>
Hola, soy {name}
;
1028 |
1029 | FooComponent.defaultProps = {
1030 | name: "laurosilvacom"
1031 | };
1032 | ```
1033 |
1034 | ## TypeScript 3.2
1035 |
1036 | [[Notas de la versión](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-2.html) | [Publicación del blog](https://blogs.msdn.microsoft.com/typescript/2018/11/29/announcing-typescript-3-2/)]
1037 |
1038 | Nada específicamente relacionado con React.
1039 |
1040 | ## TypeScript 3.3
1041 |
1042 | [[Notas de la versión](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-3.html) | [Publicación del blog](https://blogs.msdn.microsoft.com/typescript/2019/01/31/announcing-typescript-3-3/)]
1043 |
1044 | Nada específicamente relacionado con React.
1045 |
1046 | ## TypeScript 3.4
1047 |
1048 | [[Notas de la versión](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html) | [Publicación del blog](https://devblogs.microsoft.com/typescript/announcing-typescript-3-4)]
1049 |
1050 | 1. [`const` assertions](https://devblogs.microsoft.com/typescript/announcing-typescript-3-4/#const-assertions)
1051 |
1052 | ```tsx
1053 | export function useLoading() {
1054 | const [isLoading, setState] = React.useState(false);
1055 | const load = (aPromise: Promise) => {
1056 | setState(true);
1057 | return aPromise.finally(() => setState(false));
1058 | };
1059 | return [isLoading, load] as const; // infiere [boolean, typeof load] instead of (boolean | typeof load)[]
1060 | }
1061 | ```
1062 |
1063 | Más información sobre los lugares que puede usar [aserciones const](https://blog.logrocket.com/const-assertions-are-the-killer-new-typescript-feature-b73451f35802).
1064 |
1065 | ## TypeScript 3.5
1066 |
1067 | [[Notas de la versión](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html) | [Publicación del blog](https://devblogs.microsoft.com/typescript/announcing-typescript-3-5/)]
1068 |
1069 | 1. ¡Tipo incorporado de `<>` !!
1070 |
1071 | 2. Inferencia de tipo de orden superior de constructores genéricos
1072 |
1073 | ```tsx
1074 | type ComponentClass
;
1081 |
1082 | type NestedProps = { foo: number; stuff: T };
1083 |
1084 | declare class GenericComponent extends Component> {}
1085 |
1086 | // type ahora es 'new (props: NestedProps) => Component>'
1087 | const GenericComponent2 = myHoc(GenericComponent);
1088 | ```
1089 |
1090 | Consulte también [Notas de la actualización de Google a 3.5](https://github.com/microsoft/TypeScript/issues/33272)
1091 |
1092 | ## TypeScript 3.6
1093 |
1094 | [[Notas de la versión](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html) | [Publicación del blog](https://devblogs.microsoft.com/typescript/announcing-typescript-3-6/)]
1095 |
1096 | Nada es particularmente específico de React, pero [el playground](https://github.com/agentcooper/typescript-play) recibió una actualización y [Las clases Ambientales y Funciones pueden fusionarse](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html#ambient-classes-and-functions-can-merge)
1097 |
1098 | ## TypeScript 3.7
1099 |
1100 | [[Notas de la versión](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html) | [Publicación del blog](https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/)]
1101 |
1102 | 1. Optional Chaining
1103 |
1104 | ```ts
1105 | let x = foo?.bar.baz();
1106 |
1107 | // es equivalente a
1108 |
1109 | let x = foo === null || foo === undefined ? undefined : foo.bar.baz();
1110 |
1111 | // Optional Element access
1112 | function tryGetFirstElement(arr?: T[]) {
1113 | return arr?.[0];
1114 | }
1115 |
1116 | // Llamada opcional
1117 | async function makeRequest(url: string, log?: (msg: string) => void) {
1118 | log?.(`Request started at ${new Date().toISOString()}`);
1119 | const result = (await fetch(url)).json();
1120 | log?.(`Request finished at at ${new Date().toISOString()}`);
1121 | return result;
1122 | }
1123 | ```
1124 |
1125 | 2. Fusión nula
1126 |
1127 | ```ts
1128 | let x = foo ?? bar();
1129 |
1130 | // es equivalente a
1131 |
1132 | let x = foo !== null && foo !== undefined ? foo : bar();
1133 | ```
1134 |
1135 | **POR LO GENERAL DEBES USAR `??` SIEMPRE QUE USAS NORMALMENTE `||`** a menos que realmente te refieras a falsedad:
1136 |
1137 | ```tsx
1138 | function ShowNumber({ value }: { value: number }) {
1139 | let _value = value || 0.5; // reemplazará 0 con 0.5 incluso si el usuario se refiere a 0
1140 | // etc...
1141 | }
1142 | ```
1143 |
1144 | 3. Funciones de Aserción
1145 |
1146 | ```tsx
1147 | function assert(condition: any, msg?: string): asserts condition {
1148 | if (!condition) {
1149 | throw new AssertionError(msg);
1150 | }
1151 | }
1152 | function yell(str) {
1153 | assert(typeof str === "string");
1154 |
1155 | return str.toUppercase();
1156 | // ~~~~~~~~~~~
1157 | // error: La propiedad 'toUppercase' no existe en el tipo 'string'.
1158 | // Querías decir 'toUpperCase'?
1159 | }
1160 | ```
1161 |
1162 | También puede hacer assert sin una función personalizada
1163 | You can also assert without a custom function:
1164 |
1165 | ```tsx
1166 | function assertIsString(val: any): asserts val is string {
1167 | if (typeof val !== "string") {
1168 | throw new AssertionError("Not a string!");
1169 | }
1170 | }
1171 | function yell(str: any) {
1172 | assertIsString(str);
1173 |
1174 | // Ahora Typescript sabe que 'str' es de tipo 'string'
1175 | return str.toUppercase();
1176 | // ~~~~~~~~~~~
1177 | // error: La propiedad 'toUppercase' no existe el tipo 'string'.
1178 | // Querías decir 'toUpperCase'?
1179 | }
1180 | ```
1181 |
1182 | 4. `ts-nocheck`
1183 |
1184 | Ahora puedes añadir `// @ts-nocheck` a la parte superior de los archivos Typescript! bueno para las migraciones.
1185 |
1186 | ## TypeScript Roadmap y Especificaciones
1187 |
1188 | https://github.com/Microsoft/TypeScript/wiki/Roadmap
1189 |
1190 | ¿Sabía también que puede leer la especificación de TypeScript en línea? https://github.com/microsoft/TypeScript/blob/master/doc/spec.md
1191 |
1192 | # Sección 3: Misceláneas
1193 |
1194 | A veces, escribir React no se trata solo de React. Si bien no nos centramos en otras bibliotecas como Redux (ver más abajo para obtener más información al respecto), aquí hay algunos consejos sobre otras preocupaciones comunes al hacer aplicaciones con React + TypeScript.
1195 |
1196 | ## Escribir bibliotecas de TypeScript en lugar de aplicaciones
1197 |
1198 | `propTypes` puede parecer innecesario con TypeScript, especialmente cuando se compilan aplicaciones **React + TypeScript**, pero siguen siendo relevantes al escribir **bibliotecas** que pueden ser utilizadas por desarrolladores que trabajan en Javascript.
1199 |
1200 | ```ts
1201 | interface IMyComponentProps {
1202 | autoHeight: boolean;
1203 | secondProp: number;
1204 | }
1205 |
1206 | export class MyComponent extends React.Component {
1207 | static propTypes = {
1208 | autoHeight: PropTypes.bool,
1209 | secondProp: PropTypes.number.isRequired
1210 | };
1211 | }
1212 | ```
1213 |
1214 | [Algo para agregar? Abre un _issue_](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new).
1215 |
1216 | ## Componentes comentados
1217 |
1218 | TypeScript utiliza TSDoc, una variante de JSDoc para Typecript. Esto es muy útil para escribir bibliotecas de componentes y tener descripciones útiles emergentes en autocompletado y otras herramientas (como la tabla de documentos de Docz). Lo principal que debes recordar es usar la sintaxis `/** YOUR_COMMENT_HERE * /` en la línea justo encima de lo que esté anotando.
1219 |
1220 | ```tsx
1221 | import React from "react";
1222 |
1223 | interface MyProps {
1224 | /** Descripción de la "etiqueta" de props.
1225 | * @default foobar
1226 | * */
1227 | label?: string;
1228 | }
1229 |
1230 | /**
1231 | * Descripción general del componente en formato JSDoc. Markdown es *compatible*.
1232 | */
1233 | export default function MyComponent({ label = "foobar" }: MyProps) {
1234 | return
Hola mundo {label}
;
1235 | }
1236 | ```
1237 |
1238 | [Ver TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgFkBPABRzAGc4BvCnDgB6AFRi4AESQ80UYGBjAI1OBExww3OACIANigBGSfboB0Q4ZIACAEySMArvqwQIRlFCtxJYkVaGJvoA-ABccDwwCtQA5gDcFAC+FBTiYkKSAOJI1PQo+nBouJB5tHAOcgpKKmo0cABSAMpSEGhwmNAgKDDmrF4A1nYQAO51fGI8TmCQsEh2YpbkvgHkSAAes-AOzq4dTtQYtaxsAMIlqrkwABT8cEGmcAC8ep0eXrpwSRHsXBC8AEoBFYiDAnFA1AAeOzAABuAD4ABKmfQQOAjaD6OwCB76JKQkQwhGJchJIA)
1239 |
1240 | [Algo hace falta? Abre un _issue_](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new).
1241 |
1242 | ## Componentes Namespaced
1243 |
1244 | A menudo, cuando se crean componentes o componentes similares que tienen una relación _parent-child_, es útil asignar _Namespaced_ a sus componentes. Los tipos se pueden agregar fácilmente usando `Object.assign ()`;
1245 |
1246 | ```tsx
1247 | import React from "react";
1248 |
1249 | const Input = (props: any) => ;
1250 |
1251 | const Form = React.forwardRef(
1252 | ({ children, ...otherProps }, ref) => (
1253 |
1256 | )
1257 | );
1258 |
1259 | /**
1260 | * Los componentes exportados ahora se pueden usar como ``
1261 | */
1262 | export default Object.assign(Form, { Input: Input });
1263 | ```
1264 |
1265 | [Ver TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2&ssl=1&ssc=1&pln=14&pc=52#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BJGsAV3gF44AKMHMOgC44KGgE8AlHA4A+OAB5gLdnADeAOk18IAgL5wA9DIpVaDOADFoeLsnQx1maAHcUUACbJM8gBIAVAFkAGQARYAA3AFEAGyQQJBoYABoRcRlublU0AAtgaPciGhTNdQgYbKQoAAV+Ol0UokwpWR4KOAUnKDwNTTKK6tr9Ro5VRt1jcnb2rNz8wt02hQNOkAmJCQBuE3IDACpdtt24SIAPSFgkdzhqcFoEmDo4Gghna9E4ACMkOFY6S5FHgADeRWLoyQGpK7A0EgdTMNgwcGHAwUJBnaDwdxITAoVjReAAeQ+ACskBh1Cg6HRgABzGjcGEpVTw9jCFkwXSbIA)
1266 |
1267 | (Contribuido por @bryceosterhaus, ver [más discusión](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/165))
1268 |
1269 | [Algo hace falta? Abre un _issue_](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new).
1270 |
1271 | ## Desarrollo de Sistemas de Diseño
1272 |
1273 | Me gusta [Docz](https://docz.site/), que toma básicamente [1 línea de configuración](https://www.docz.site/documentation/project-configuration#typescript) para aceptar Typecript. Sin embargo, le falta mucho trabajo, se vienen muchos cambios importantes ya que todavía es . Esto incluye la generación automática de documentación de _types_, lo cual es increíble :)
1276 |
1277 | [Algo para agregar? Presentar un problema](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new).
1278 |
1279 | ## Migrando desde Flow
1280 |
1281 | Debe consultar los proyectos grandes que están migrando desde Flow para obtener referencias and tips:
1282 |
1283 | - [Jest](https://github.com/facebook/jest/pull/7554)
1284 | - [Expo](https://github.com/expo/expo/issues/2164)
1285 | - [React-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd/issues/982)
1286 | - [Storybook](https://github.com/storybooks/storybook/issues/5030)
1287 | - [VueJS](https://medium.com/the-vue-point/plans-for-the-next-iteration-of-vue-js-777ffea6fabf)
1288 |
1289 | Bibliotecas útiles:
1290 |
1291 | -
1292 | -
1293 | -
1294 |
1295 | Si tiene consejos específicos en esta área, ¡presente un PR!
1296 |
1297 | [Algo hace falta? Abre un _issue_](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new).
1298 |
1299 | ## Prettier
1300 |
1301 | No hay ningún secreto real para Prettier para TypeScript. ¡Pero es una gran idea correr Prettier en cada commit!
1302 |
1303 | ```js
1304 | yarn add -D prettier husky lint-staged
1305 |
1306 | // inside package.json
1307 | {
1308 | //...
1309 | "husky": {
1310 | "hooks": {
1311 | "pre-commit": "lint-staged"
1312 | }
1313 | },
1314 | "lint-staged": {
1315 | "linters": {
1316 | "src/*.{ts,tsx,js,jsx,css,scss,md}": [
1317 | "prettier --trailing-comma es5 --single-quote --write",
1318 | "git add"
1319 | ],
1320 | "ignore": [
1321 | "**/dist/*, **/node_modules/*"
1322 | ]
1323 | }
1324 | },
1325 | "prettier": {
1326 | "printWidth": 80,
1327 | "semi": false,
1328 | "singleQuote": true,
1329 | "trailingComma": "es5"
1330 | }
1331 | }
1332 | ```
1333 |
1334 | Integrar esto con ESlint puede ser un problema. Todavía no hemos escrito mucho sobre esto, por favor contribuya si tiene una opinión firme. [Aquí hay un gist útil.](https://gist.github.com/JirkaVebr/519c7597517e4ba756d5b89e7cb4cc0e)
1335 |
1336 | Esto está configurado para ti en [tsdx](https://github.com/palmerhq/tsdx/pull/45/files).
1337 |
1338 | ## Testing
1339 |
1340 | ¡Sí, puedes probar tus tipos! No debe usarlo para TODO, pero puede ayudar a prevenir regresiones:
1341 |
1342 | - https://github.com/azz/jest-runner-tsc
1343 | - https://github.com/SamVerschueren/tsd
1344 | - https://github.com/ikatyang/dts-jest ([Demo](https://codesandbox.io/s/dts-test-frozen-public-demo-iyorn))
1345 | - https://github.com/microsoft/dtslint ([Intro to dtslint](https://www.youtube.com/watch?v=nygcFEwOG8w&feature=share)
1346 |
1347 | ## Trabajar con bibliotecas que no son de TypeScript (escribe tu propio index.d.ts)
1348 |
1349 | Digamos que deseas usar `de-indedent`, pero no está escrito o en DefinitelyTyped. Obtienes un error como este:
1350 |
1351 | ```
1352 | [ts]
1353 | Could not find a declaration file for module 'de-indent'. '/Users/swyx/Work/react-sfc-loader/node_modules/de-indent/index.js' implicitly has an 'any' type.
1354 | Try `npm install @types/de-indent` if it exists or add a new declaration (.d.ts) file containing `declare module 'de-indent';` [7016]
1355 | ```
1356 |
1357 | Así que sea crea un archivo `.d.ts` en cualquier parte de tu proyecto con la definición del módulo:
1358 |
1359 | ```ts
1360 | // de-indent.d.ts
1361 | declare module "de-indent" {
1362 | function deindent(): void;
1363 | export = deindent; // exportación predeterminada
1364 | }
1365 | ```
1366 |
1367 |
1368 |
1369 | Más discusión
1370 |
1371 | ¿Algún otro consejo? ¡Por favor contribuya en este tema! [mas referencias](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/8). Tenemos más discusión y ejemplos [en nuestro _issue_ aquí](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/12).
1372 |
1373 |
1374 |
1375 | # Sección 4: @types/react y @types/react-dom APIs
1376 |
1377 | Los tipings `@ types` exportan tanto los tipos "públicos" destinados a su uso como los tipos "privados" que son para uso interno.
1378 |
1379 | Consulte la [Hoja de referencia de React TypeScript de SaltyCrane](https://github.com/saltycrane/typescript-cheatsheet) para obtener una buena referencia completa autogenerada.
1380 |
1381 | ## `@types/react`
1382 |
1383 | [Enlace a `.d.ts`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts)
1384 |
1385 | **Namespace: React**
1386 |
1387 | Interfaces y tipos más utilizados:
1388 |
1389 | - `ReactNode` - cualquier cosa que sea renderizabl*dentro* de JSX, ¡esto NO es lo mismo que lo que puede representar un componente!
1390 | - `Component` - clase base de todos los componentes basados en clases
1391 | - `PureComponent` - clase base para todos los componentes optimizados basados en clases
1392 | - `FC`, `FunctionComponent` - Una interfaz completa para componentes de funciones, a menudo utilizada para escribir componentes externos en lugar de escribir los tuyos
1393 | - `CSSProperties` - usado para escribir objetos de estilo
1394 | - all events: se usa para escribir controladores de eventos
1395 | - all event handlers: se usa para escribir controladores de eventos
1396 | - all consts: `Children`, `Fragment`, ... son todos públicos y reflejan el espacio de nombres React runtime
1397 |
1398 | No se usa comúnmente pero es bueno saberlo
1399 |
1400 | - `Ref` - solía escribirse `innerRef`
1401 | - `ElementType` - utilizado para componentes de orden superior u operaciones en componentes
1402 | - `ComponentType` - utilizado para componentes de orden superior donde no se trata específicamente con los componentes intrínsecos
1403 | - `ReactPortal` - se usa si necesita específicamente escribir un accesorio como portal, de lo contrario es parte de `ReactNode`
1404 | - `ComponentClass` - Una interfaz completa para la función de constructor producida de una declaración de clase que extiende `Componente`, a menudo utilizada para escribir componentes externos en lugar de escribirla.
1405 | - `JSXElementConstructor` - todo lo que TypeScript considere válido puede entrar en la etiqueta de apertura de una expresión JSX
1406 | - `ComponentProps` - props de un componente
1407 | - `ComponentPropsWithRef` - props de un componente donde una clase se base deun componentn `ref` prop con su propia instancia de type
1408 | - `ComponentPropsWithoutRef` - accesorios de un componente sin el apoyo de `ref`
1409 | - all methods: `createElement`, `cloneElement`, ... son todos públicos y reflejan la API de tiempo de ejecución de React
1410 |
1411 | [@Nota de Ferdaber](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/69): Desaliento el uso de la mayoría de los tipos `... Element` debido a lo desconocido de `JSX .Element`. Casi siempre debes suponer que cualquier cosa producida por `React.createElement` es el tipo base `React.ReactElement`.
1412 |
1413 | **Namespace: JSX**
1414 |
1415 | - `Element` - El tipo de cualquier expresión JSX
1416 | - `LibraryManagedAttributes` - Especifica otros lugares donde los elementos JSX pueden declarar e inicializar tipos de _props_. Se usa para resolver `defaultProps` y `propTypes` estáticos con el tipo de accesorios internos de un componente.
1417 | - `IntrinsicElements` - todos los componentes integrados posibles que se pueden escribir como un type en minúscula en JSX
1418 |
1419 | No se usa comúnmente pero es bueno saberlo
1420 |
1421 | - `IntrinsicAttributes` conjunto de atributos que admiten todos los `IntrinsicElements` ... básicamente solo `key`.
1422 | - `ElementChildrenAttribute` nombre de la propiedad que TS examina para determinar qué tipos de _children_ admite un componente. Básicamente la propiedad `children`
1423 | - `ElementAttributesProperty` nombre de la propiedad que TS observa para descubrir qué atributos admite un componente. Básicamente la propiedad `props` (para una instancia de clase)
1424 |
1425 | **No usar Interno/Desaprobado**
1426 |
1427 | Cualquier cosa que no esté en la lista anterior se considera de tipo interno y no público. Si no está seguro, puedes consultar la fuente de `@types/react`. Los tipos se anotan en consecuencia.
1428 |
1429 | - `SFCElement`
1430 | - `SFC`
1431 | - `ComponentState`
1432 | - `LegacyRef`
1433 | - `StatelessComponent`
1434 | - `ReactType`
1435 |
1436 | ### Agregando Atributos No Estandarizados
1437 |
1438 | Los atributos permitidos en los componentes del host como `button` o`img` siguen el
1439 | Estándar de vida HTML. Nuevas características que aún no forman parte de la especificación
1440 | o solo son implementados por ciertos navegadores, por lo tanto, causarán un error de tipo. Si
1441 | tu específicamente escribes código para estos navegadores usa [módulo de aumento](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation) para seguir comprobando el tipo de componentes sin tener que
1442 | usar `any` o`@ ts-ignore`.
1443 |
1444 | En este ejemplo, agregaremos el atributo [`loading`](https://www.chromestatus.com/feature/5645767347798016) que agrega soporte para [lazy-loading](https://web.dev/native-lazy-cargando) imágenes en Chrome:
1445 |
1446 | ```ts
1447 | // react-unstable-attributes.d.ts
1448 | import "react";
1449 |
1450 | declare module "react" {
1451 | interface ImgHTMLAttributes extends HTMLAttributes {
1452 | loading?: "auto" | "eager" | "lazy";
1453 | }
1454 | }
1455 | ```
1456 |
1457 | ## @types/react-dom
1458 |
1459 | Para ser escrito
1460 |
1461 | # Mi pregunta no se responde aquí!
1462 |
1463 | - [Abre un issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new).
1464 |
--------------------------------------------------------------------------------