Video Summary and Transcription
React y TypeScript tienen una relación fuerte, con TypeScript ofreciendo beneficios como un mejor control de tipos y cumplimiento de contratos. Fallar temprano y fallar duro es importante en el desarrollo de software para detectar errores y depurar de manera efectiva. TypeScript proporciona una detección temprana de errores y garantiza la precisión de los datos en los componentes y hooks. Ofrece una seguridad de tipo superior pero puede volverse complejo a medida que crece la base de código. Usar tipos de unión en props puede resolver errores y abordar dependencias. La comunicación dinámica y los contratos de tipo se pueden lograr a través de genéricos. Comprender los tipos incorporados de React y los hooks como useState y useRef es crucial para aprovechar su funcionalidad.
1. Introducción a React y TypeScript
React y TypeScript son como amantes predestinados. Muchas personas entran en un período de luna de miel con React y TypeScript, pero eventualmente se dan cuenta de que algunas cosas no encajan. Sin embargo, hay un secreto para un matrimonio feliz entre React y TypeScript. TypeScript es enormemente popular en la comunidad de React, y ofrece beneficios que los archivos JSX y los tipos de prop no proporcionan. TypeScript proporciona una mejor comprobación de tipos, autocompletado y hace cumplir los contratos a nivel de componente. También funciona a la perfección con los hooks de React, que son esenciales en el desarrollo moderno de React.
♪♪ Dos casas, ambas igualmente dignas, en la justa VS code, donde situamos nuestra escena. De las fatales entrañas de estos dos enemigos, una pareja de amantes predestinados toman su vida. React y TypeScript, amantes predestinados, creo. Sabes, creo que son almas gemelas. Pero muchas personas, entran en un período de luna de miel con React y TypeScript, los juntan, y empiezan a darse cuenta de que algunas cosas realmente no encajan. Y nunca llegan a cumplir con la relación y nunca llegan a la vejez.
Así que quiero contarles el secreto de un matrimonio feliz entre React y TypeScript, cómo sobrevivir al período de luna de miel y la decepción posterior. Soy el Rodney Molinow de TypeScript, aparentemente. Esa es mi especie de eslogan en Twitter. Así es como todos me conocen. Soy mattpoco.uk en Twitter. Estoy construyendo un curso llamado totaltypescript.com, que parece increíblemente asombroso. Mira todas estas ilustraciones, que van a cubrir una gran cantidad de TypeScript, esperemos que todo, básicamente, desde todas las cosas de principiante hasta todas las cosas avanzadas. Y también trabajo en Vassell en un producto diferente llamado TurboRepo, que también es muy bueno.
Entonces, ¿por qué usarías TypeScript en React? ¿Por qué no usar simplemente JSX? Bueno, debe haber algo en esto porque TypeScript es enormemente popular. Y si estás construyendo una aplicación de React en este momento, estás eligiendo usar TypeScript o probablemente estás eligiendo no usar TypeScript. Pero TypeScript siempre está en la conversación. Entonces, ¿por qué no usar simplemente archivos JSX? Bueno, hay una página en los docs de React, que es básicamente la comprobación de tipos en React. Y está usando algo llamado tipos de prop. Y con los tipos de prop, básicamente puedes darle a un componente, una especie de interfaz para decir así es como se supone que debes llamar a ese componente. Y no está mal. Quiero decir, si usas esto y si dices, vale, quiero usar esta entrada aquí entonces realmente te da un autocompletado en el nombre aquí. Y si cambio esto a ID, obtendré autocompletado en el ID también. Así que no es terrible, pero también es como, cuando miras a TypeScript, no vas a mirar esto de nuevo. Lo siguiente es como, como que realmente no hace cumplir un contrato a nivel de componente. Te da algunas sugerencias y te permite, documenta un poco lo que hace, pero los tipos de prop en realidad no dan error. Simplemente arroja un error en la consola. Y si no estás mirando la consola, entonces realmente no vas a ver nada de esto. Tampoco funciona con los hooks. Y los hooks son como la moneda principal de React desde que se lanzaron.
2. Importancia de Fallar Temprano y Fallar Duro
Fallar en la consola es demasiado tarde. Quieres detectar los errores lo más pronto posible. Fallar temprano facilita la depuración y la extracción del error. Fallar duro significa no ignorar los errores. Es importante fallar temprano y fallar duro.
Y fallar en la console es realmente demasiado tarde para un fallo. Realmente quieres obtener ese error lo más pronto posible. Y si no estás revisando la console, entonces podrías simplemente enviar ese code a producción y podría fallar. Y la razón de esto es, es que siempre quieres fallar antes. Cuanto antes falles, más fácil es ver qué sucedió. Porque si fallas demasiado tarde y ese error y esa mala cosa, como ese mal bit de data que pasas a tu componente pasa por tu sistema, entonces es realmente, realmente difícil de debug. Es como si estuviera enterrado en tu sistema y tienes que extraerlo. Y cuanto más duro falles, entonces más difícil es de ignorar también. Si simplemente estás lanzando un error en la console, entonces eso realmente no va a hacer el trabajo. Idealmente, quieres fallar temprano y fallar duro, justo como mi mamá siempre decía.
3. Fallar Temprano y Fallar Duro en TypeScript
En TypeScript, puedes asegurar la precisión de los datos pasados a los componentes y hooks definiendo sus tipos en línea. Esto permite la detección temprana de propiedades faltantes o incorrectas, resultando en una aplicación más robusta.
¿Y qué significa realmente fallar duro? Bueno, en tu aplicación, cada vez que tienes un componente, cada vez que tienes un hook, básicamente estás llamando a ese componente o hook. Sabes, es como una función. Y cuando haces eso, realmente quieres que las cosas que pasas sean precisas. Y me gusta pensar en esto como las costuras de tu aplicación. Como los componentes y los hooks, son como los trozos de tela que estás uniendo al llamarlos. Sabes, mi esposa es como, ella hace costura y cosas así. Así que estoy seguro de que ella está en la siguiente habitación preguntándose, ¿de qué diablos estás hablando? Pero la idea es que si tus costuras son fuertes, entonces tu aplicación será más robusta. Y así es como lo haces en TypeScript. Así que tienes esta entrada aquí, digamos, y en lugar de hacer esto con prop types, puedes hacer esto en línea aquí. Así que puedes decir props named string. Y ahora si no paso esto, entonces me va a gritar, lo cual es agradable. La propiedad name falta en el tipo blue, requerida en el tipo blue. Y también es como, si le paso lo incorrecto, también me va a gritar. Así que si lo paso con un número aquí.
4. Beneficios y Desafíos de Usar TypeScript
TypeScript permite la detección temprana de errores mientras escribes, proporcionando una experiencia de desarrollo robusta y eficiente. Admite la declaración en línea de props y hooks, con la capacidad de especificar tipos para parámetros y valores de retorno. TypeScript ofrece una seguridad de tipo superior en comparación con los tipos de prop y ayuda a establecer una base sólida para tu base de código. Sin embargo, a medida que tu base de código crece, la complejidad de los contratos de función puede convertirse en un factor de estrés. Es necesario considerar cuidadosamente las llamadas a funciones y sus contratos para mantener una relación saludable y duradera entre TypeScript y tu proyecto.
Y esto es como lo más temprano que podrías imaginar fallar. Está fallando mientras escribes. Y mucha gente dice, Dios, realmente no me gusta esto en absoluto. Es como si mi profesor de inglés estuviera, mirándome por encima y diciendo, no, hiciste esto mal, ¿cómo te atreves? Pero me encanta esto porque está fallando tan temprano como puede fallar. Y está fallando muy duro también. Te está dando esta horrible advertencia justo ahí. Y a menudo, si conectas esto a tu CI, entonces no te va a dejar empujar esto.
Entonces puedes declarar props en línea como esto, o puedes declararlos con una interfaz como esta, o con un tipo también, tipo o interfaz, no importa. Y luego también puedes hacer esto para los hooks. Así que este use inputs aquí, tiene un objeto params, que tiene este valor predeterminado string. También puedes especificar funciones aquí. Así que valor string Boolean. Y puedes consultar los TypeScript docs para aprender más sobre esto también. Y si devuelvo lo incorrecto de esto, si solo devuelvo un número aquí, entonces me va a gritar porque el número no es un tipo simple Boolean. Así que no son solo funciones y lo siento, hooks y componentes, son todas las funciones, realmente. Y un montón de otras cosas también. Así que de nuevo, puedes declarar esto con interfaz o puedes declarar esto con un tipo también.
Entonces podrías pensar que estoy enamorado. Esto es mucho mejor que los tipos de prop, basura con la que estaba lidiando antes. Pero suave, ¿qué luz a través de la ventana rompe es type safety y TypeScript es el sol. Y esto es lo que los expertos llaman período de luna de miel. Este es el momento en tu relación antes de que empieces a compartir una nevera. Y a medida que avanzas aquí, empiezas a pensar, está bien, ¿cómo hacemos que esta relación dure? ¿Cuáles son los factores de estrés en esta relación? ¿Cómo va a sobrellevar esto en el futuro? Ahora, el gran factor de estrés que entra es que los contratos que especificas en estas escenas, tus llamadas a funciones a veces pueden ser realmente, realmente complicados. Y si piensas en este problema aquí, tenemos un botón y es como uno de esos botones que podrías haber visto antes donde si pasas un ref, sé que es href, pero siempre lo llamo ref, así que aguanta conmigo, entonces se renderiza como un ancla. Si no pasas un ancla, oh, lo siento, si no pasas un href y pasas un onClick en su lugar, entonces se renderiza como un botón. Y así puedes, como, unifica un poco tu estilo y significa que puedes hacer como botones y anclas y diferentes cosas. Pero imagina que accidentalmente pasas un href y un onClick. Hmm, ¿qué va a pasar aquí? Bueno, este onClick en realidad va a ser ignorado debido a la lógica que está aquí arriba. Y este es un archivo JavaScript, así que no nos está dando nada.
5. Props Dinámicas y Tipos Unión en TypeScript
Podemos usar propiedades opcionales en TypeScript para permitir el paso de href o onClick, o ninguno. Sin embargo, este enfoque puede llevar a errores. Para resolver esto, podemos usar un patrón de props dinámicas con tipos unión, que permite la existencia separada de href y onClick. Al combinar las props normales con los children y las props dinámicas, podemos crear un botón que obliga a la presencia de href o onClick. Este enfoque resuelve los errores tanto dentro como fuera de los componentes, proporcionando una solución más robusta. Los tipos unión en las props ofrecen una alternativa más sencilla a las herramientas más complejas y pueden abordar las dependencias entre las props.
También podemos no pasar nada a esto si queremos. Y esto parece absurdo porque ahora tenemos un botón que no hace nada. Y realmente nuestros botones, queremos que estén haciendo hrefs y onClicks.
Entonces, ¿cómo resolveríamos esto en TypeScript? Bueno, ya sabes, estás en tu período de luna de miel y estás pensando, vaya, soy una persona hermosa y maravillosa. Así que usas propiedades opcionales. Dices, está bien, no necesitas pasar un href y no necesitas pasar un onClick. Y simplemente los añadiremos así. Y de nuevo, tu lógica se mantiene igual, lo cual es agradable. Pero esto de nuevo, como que es un poco sospechoso. Tanto el href como el onClick aún pueden ser proporcionados o aún podemos no pasar ninguno. Estos son aún válidos.
Entonces, ¿cómo hacemos que TypeScript vaya con nosotros y diga que puedes pasar un href o un onClick? Bueno, puedes usar este tipo de patrón de props dinámicas. Y lo que esto está haciendo es decir que puedes pasar un href que es un string aquí, o puedes pasar un onClick void aquí. Y básicamente está diciendo que estos dos objetos pueden existir de forma separada. Y entonces lo que decimos es que combinamos nuestras props normales de aquí arriba con los children que tenemos y las props dinámicas. Y así terminas con un botón donde tienes que ignorar estos errores por ahora, que realmente da error cuando no asignas ninguno. Así que ahora podemos pasar un href o podemos pasar un onClick, lo cual es muy, muy agradable.
Pero tenemos un problema, que es que en realidad está dando error dentro de los componentes. Podríamos hacer algunas cosas raras dentro de aquí, podemos decir props como cualquier cosa, pero entonces tendríamos que hacer eso en todas partes. Entonces, ¿por qué está dando error? Bueno, está diciendo que puedes pasar un objeto con un href o un objeto con un onClick, y en realidad no puedes reducir el objeto de props simplemente accediendo a algo en él. Como que TypeScript no te deja hacer eso. Así que necesitamos refactorizar esto ligeramente para usar una comprobación aquí. Entonces, si href está en props, que esto es válido en JavaScript, entonces lo que hace es comprobar que tiene un atributo href dentro de props, y entonces eso significa que en realidad tienes href aquí, lo cual es agradable. Y significa que, ya que estamos retornando dentro de aquí, entonces props, tiene un href en él en este cierre, pero en realidad tiene un props.onClick en este cierre. Así que TypeScript es realmente útil aquí. Y eso ahora significa que está contento fuera de los componentes y contento dentro de los componentes, lo cual es genial. Y esto se llama un tipo unión, y realmente recomiendo usar estos. Los tipos unión en las props pueden resolver muchos problemas para los que crees que necesitas herramientas más complicadas, pero en realidad solo necesitas un tipo unión. Hay más cosas aquí. Otro problema que puede ocurrir es cuando tienes, tienes algunas props que crees que dependen la una de la otra.
6. Entendiendo la Seguridad de Tipos y Problemas de Sincronización
Pasamos las filas a nuestra tabla, nombre John, nombre Vakas, y luego tenemos render row. Pero esta fila, su tipo es desconocido. Así que terminaremos teniendo que hacer como name string, solo para hacer feliz a la otra persona. Pero realmente hemos perdido un poco de seguridad de tipos aquí. Y como si agregamos un ID aquí, ya sabes, número de ID, entonces esto no está realmente presente en las filas de arriba, por lo que estos dos pueden desincronizarse.
Tenemos filas aquí y luego render row y lo usamos más o menos así. Pasamos las filas a nuestra tabla, nombre John, nombre Vakas, y luego tenemos render row. Pero esta fila, su tipo es desconocido. Ahora eso es tonto, eso es estúpido. ¿Por qué no me entendiste? Como sabemos que es este tipo, ¿verdad? Así que terminaremos teniendo que hacer como name string, solo para hacer feliz a la otra persona. Pero esto en realidad no es asignable. Entonces, el tipo desconocido no es asignable a ese tipo. Así que terminamos teniendo que subir aquí y hacer cualquier cosa en su lugar. Pero realmente hemos perdido un poco de type safety aquí. Y como si agregamos un ID aquí, ya sabes, número de ID, entonces esto no está realmente presente en las filas de arriba, por lo que estos dos pueden desincronizarse. Y este tipo de sensación de desincronización es realmente, realmente peligrosa para la relación.
7. Comunicación Dinámica y Contratos de Tipo
La solución es comunicarse de manera dinámica y asegurar que las props pasadas al componente de la tabla coincidan con la estructura esperada. Al usar genéricos, podemos crear un nuevo tipo que refuerza la estructura correcta de las props. Esto permite la creación de poderosos contratos dinámicos y permite la reutilización de componentos de tabla en varios contextos.
Entonces la solución es, y de hecho, permíteme arreglar esto desde el otro ángulo. La solución aquí es comunicarse de manera dinámica y decir, cuando llamas a esta tabla, voy a escucharte. TypeScript te va a escuchar y decir, de acuerdo, lo que sea que esté aquí también necesita ir aquí. Así que estas dos cosas, sean lo que sean, van a ser la misma cosa. Y luego puedes agregar esto, esto se llama un genérico aquí arriba. Y es algo así como un argumento de tipo. Entonces, si quisiera crear un nuevo tipo a partir de esto, puedo decir que mis props de tabla son iguales a las props de la tabla, y puedo pasar cualquier cosa a esto. Puedo decir nombre, cadena, número de ID, lo que sea, y luego termino con filas, o en realidad si voy const props son mis props de tabla, entonces puedo decir que tenemos filas, y eso ahora debe ser ID y nombre. Entonces, esto es algo así como una forma de insertar un tipo en otro tipo. Esto convierte las props de la tabla en una función. Y entonces puedo decir t row dentro de aquí, y voy a agregar t row a este pequeño espacio genérico aquí. Eso me permite, si quiero, pasar argumentos de tipo junto con mis argumentos regulares a esta función. Ahora eso, lo que va a hacer es que si no terminas pasando un argumento de tipo, simplemente lo va a inferir de aquí arriba. Y con lo que terminamos es que si borro este bit aquí, ahora tenemos named jar named vacas, y la fila está tipada como nombre cadena. Si pasas el cursor sobre esta tabla aquí, puedes ver que tiene este pequeño t row aquí, que es muy, muy útil. Y si agregamos otra propiedad a este ID uno, por ejemplo, entonces voy a poder acceder al ID en esto también. Así que puedo decir que la clave es ahora row dot ID, poniendo para dar a vacas un ID dos, ahí vamos. Extremadamente útil para cuando tienes diferentes propiedades en el mismo tipo de objeto props, que todos dependen entre sí. Puedes agregar este tipo de sintaxis genérica aquí e incluso puedes asegurarte de que corresponde a cierta cosa. Entonces, si siempre quieres pasar como un ID aquí, entonces va a hacer cumplir esa propiedad allí. Así que ahora estamos diciendo que T row debe extender ID número. Esta palabra clave extiende es realmente horrible, aparece en un montón de lugares. Pero lo que significa es que esto está restringido a ser esta forma ahora. Entonces, si no paso un ID aquí, entonces me va a gritar porque props ID nombre no es asignable al tipo ID número. Y ahora tengo que pasar un ID, y eso significa que siempre tendré una clave aquí también. Muy, muy útil. Entonces, esta idea es como, es básicamente un genérico en una prop de componente. Y es realmente poderoso para sincronizar tipos juntos al mismo componente esencialmente, para que termines con estos contratos dinámicos realmente poderosos. Puedo ir y reutilizar mis componentes de tabla en un montón de lugares diferentes. Y no importa qué data estoy pasando el componente será capaz de entenderlo.
8. Los tipos incorporados de React y useState
Los tipos incorporados de React, mantenidos por separado de React en sí, pueden causar confusión. Pasar un argumento de tipo a useState puede asegurar que el estado esté tipado correctamente. La primera renderización puede resultar en indefinido si no se pasa ningún valor. Comprender las definiciones de tipo para hooks como useState es esencial para aprovechar su funcionalidad.
Porque realmente esto es solo un envoltorio alrededor de esta función de renderizado de fila. He usado este patrón antes en producción es increíblemente asombroso.
Entonces, otra cosa que puede atraparte son los tipos incorporados de React. Ahora, estos se mantienen por separado de React en sí. React no está escrito en TypeScript. Está escrito en Flow, creo. Y lo que hace entonces es que tienes que instalar React y luego instalar los tipos de React. Y el tipo de, porque no están construidos juntos, a menudo pueden hacer algo ligeramente diferente a la base de React, no de una manera peligrosa, pero hay algunas cosas que están en los tipos que React en sí no ha documentado. Y esto puede ser realmente, realmente doloroso.
Entonces, vamos con uno simple aquí, que es digamos que tienes un número en nuestro estado. Ahora, como se predijo, si comienzas con un número en tu USE state aquí, entonces este estado va a ser tipado como número y el estado establecido también será tipado como número. Así que no puedo pasar una cadena a esto, voy a tener que pasar un número. Y estos son todos parámetros incorporados en React, por supuesto, si los usas.
Ahora, ¿qué pasa entonces si no tengo un valor para pasar aquí, pero aún quiero que este estado esté tipado como un número? Bueno, puedes pasar esto como un argumento de tipo. Y entonces puedes decir, bueno, este estado ahora es un número. Y podrías pensar que el estado ahora está tipado como número porque puedo establecer el estado a número, pero no es así, en realidad está tipado como número o indefinido. Y eso es correcto porque la primera renderización aquí, antes de establecer mi estado, imagina si hago esto en un, sé que esto es una mala práctica hoy en día, no muy de moda, pero digamos que simplemente establezco mi estado a uno dentro de este use effect. Bueno, en la primera renderización, el estado en realidad va a ser indefinido porque no estoy pasando nada a esto. Y mientras que si realmente le paso un número, entonces va a estar definido en esa primera renderización. Ahora, la forma en que puede hacer esto, la forma en que sabe cómo hacer esto es si hacemos clic en el comando en el estado aquí, terminamos en este archivo muy, muy aterrador. Y por supuesto, inmediatamente perdí mi lugar donde básicamente estamos dentro de las definiciones de tipo para todos estos hooks. Así que tenemos use context, tenemos use state, ya sabes, oh, hola chicos, toda la pandilla está aquí. Y lo que tenemos entonces, es que tenemos una sobrecarga de función en useState. El estado puede ser llamado de dos maneras diferentes dependiendo de lo que quieras, básicamente. Entonces, esta primera sobrecarga aquí, esto es realmente difícil de leer. Así que déjame desglosarlo un poco. Lo que está pasando es que useState, es una función genérica, hemos visto un genérico antes. Y está diciendo que el estado inicial es esta S o una función que devuelve S. No guardes mis cambios, por favor. No quiero ser responsable de romper nada.
9. Entendiendo useState y useRef
En useState, si no se proporciona un argumento de tipo, el tipo de retorno puede ser S o indefinido. Es importante usar useState correctamente para evitar tipos indefinidos inesperados. useRef tiene múltiples sobrecargas, incluyendo una donde se pasa el valor inicial y devuelve un objeto ref mutable. Otra sobrecarga acepta un argumento de tipo de número y nulo, evitando la reasignación. Comprender el uso de nulo en useRef es crucial para controlar las refs en el desarrollo de React. Si quieres aprender más sobre TypeScript y React, no dudes en conectarte conmigo en Twitter.
Eso significa entonces que puedo analizar este argumento de tipo y puedo tener una función que devuelve un número o simplemente puedo analizar el número en sí. Tiene sentido hasta ahora. Ahora, la segunda sobrecarga dice que si no analizo nada, entonces lo que se devuelve es S o indefinido, mientras que aquí arriba, era solo S. Y lo que eso significa entonces es que si no analizo nada aquí, voy a terminar con número o indefinido. Y solo hay dos sobrecargas en useState, lo cual es bastante agradable, pero es bastante importante hacerlo bien para no terminar con indefinido invadiendo accidentalmente tus tipos.
El realmente horrible, sin embargo, es useRef. Así que vamos a useRef como este es un error realmente desagradable. Es un debug. Inmediatamente. Asqueroso. Objeto ref mutable, objeto ref legado. Blah, blah, blah. Y la solución a esto es profundamente contraintuitiva. ¡Whoa! Ahí lo tienes, eso es. ¿Por qué funciona eso? Bueno, si vamos a useRef, entonces veremos que hay varias sobrecargas en useRef, donde lo que tenemos es que tenemos tres sobrecargas. Tenemos un useRef donde el valor inicial es t y se pasa y terminas con un objeto ref mutable. Entonces, si miramos dentro, ¿dónde está? Aquí está. Tenemos nuestro número aquí y luego Sobrecarga1, terminas con React Objeto Ref Mutable. Así que si digo Sobrecarga1.Current, puedo asignar esto a algo diferente, pero en Sobrecarga2 aquí, si paso un número o lo siento, si paso un argumento de tipo de número, pero paso nulo, puedo decir Sobrecarga2.Current igual a tres o lo que sea, no puedo reasignarlo. ¿Qué? Quiero decir, esto parece exactamente lo mismo. Seguramente esto debería ser tipado como Sobrecarga2 es como cosa o nulo, ¿verdad? Bueno, te mostraré por qué esto sucede en un segundo o por qué esto está aquí. Y si no paso nada, entonces me va a dejar ser mutable de nuevo. Así que Sobrecarga3.Current igual a dos. Entonces, la razón de que esto esté en su lugar es que, como hay algunas refs que React necesita controlar y hay algunas refs que puedes controlar. Y en general, las que React quiere controlar es donde las estás pasando directamente al desarrollo esencialmente. Y la forma en que React te dice que hagas esto es con esta propiedad nula. Entonces, si no pasas nulo, te va a gritar pero si pasas nulo te va a gritar. Y este tipo de useRef son realmente importantes para entender porque esto realmente puede darte mucho dolor en términos de cuando estás usando React y usando estas refs.
Pero creo que básicamente este es mi tiempo. Si quieres aprender más sobre esto entonces ven a hablar conmigo después y encuéntrame en Twitter y pregúntame sobre TypeScript y React. Hay mucho en lo que puedes profundizar y es un tema realmente, realmente profundo. Pero realmente creo que puede ser un matrimonio exitoso si resuelves estas dificultades y realmente entiendes de dónde vienen ambas partes. Gracias.
Comments