Video Summary and Transcription
Esta charla trata sobre la implementación de un subconjunto limitado del equivalente de tipo de React, específicamente su motor JSX, en el sistema de tipos de TypeScript sin código en tiempo de ejecución. El orador demuestra cómo usar características de TypeScript como tipos restringidos y cadenas de texto literales para inferir y renderizar elementos JSX en el sistema de tipos. También muestran cómo renderizar encabezados e hijos utilizando una utilidad llamada 'render component'. La charla concluye con recursos adicionales para aprender TypeScript y temas relacionados.
1. Introducción al sistema de tipos React
Hola y bienvenidos al sistema de tipos React conmigo, Josh Goldberg. Soy un mantenedor de código abierto a tiempo completo en el ecosistema TypeScript. Vamos a implementar un subconjunto limitado del equivalente de tipo de React, solo su motor JSX en el sistema de tipos sin código en tiempo de ejecución. Comencemos en TypeScript lang dot org slash play.
Hola y bienvenidos al sistema de tipos React conmigo, Josh Goldberg. Soy un mantenedor de código abierto a tiempo completo en el TypeScript ecosistema. Trabajo en proyectos generales que te ayudan a escribir TypeScript un poco mejor, más notablemente TypeScript ESLint, la herramienta que te permite ejecutar herramientas estándar de JavaScript como ESLint y Prettier en código TypeScript. También soy el autor del libro de aprendizaje de TypeScript a través de O'Reilly, por lo que me gusta hablar sobre TypeScript. Todo lo que vamos a charlar hoy está en GitHub y es de código abierto bajo el sistema de tipos React, un repositorio en mi usuario Josh Goldberg, pero quiero señalar que esto no es TypeScript normal. No necesitas seguir al pie de la letra y entender todo completamente para ser un desarrollador de TypeScript o para trabajar de manera competente en TypeScript. Hoy todo son travesuras, y aunque los conceptos que voy a mostrar son realmente útiles para trabajar en el sistema de tipos si lo haces mucho, no son cosas que deberías usar día a día. Son todas cosas tontas, todas las travesuras extrañas.
Porque lo que vamos a hacer es implementar un subconjunto muy limitado del equivalente de tipo de React, realmente solo su motor JSX en el sistema de tipos sin código en runtime. Vamos a hacer un tipo, digamos un registro de componentes. Vamos a hacer un tipo de ayuda render que toma una cadena y devuelve los resultados JSX, los resultados renderizados, y vamos a imprimir eso en las herramientas de desarrollo que están ejecutando TypeScript para nosotros. Así que muy poco práctico. Nunca satisfarías ninguna necesidad del usuario con esto, pero creo que es una forma linda de explorar el sistema de tipos. Así que comencemos. Puedes encontrar todo el código que voy a enviar mi código en vivo bajo el repositorio bajo fuente slash fun, pero voy a estar trabajando en TypeScript lang dot org slash play, que es un patio de juegos realmente agradable disponible en el sitio web de TypeScript. Puedes escribir código TypeScript a la izquierda y obtener código JavaScript o cualquier error, archivos de declaración a la derecha. Ahora a la izquierda, tengo solo algo de código estándar TypeScript. Tengo un registro de componentes de tipo y un registro de consola, y podemos ver que la representación del sistema de tipos solo incluye el tipo y la representación de JS solo incluye el JavaScript. Pero mencioné que hoy es puramente en el sistema de tipos. Así que voy a seguir adelante y eliminar nuestro registro de consola. No JavaScript solo tipos. Pero eso plantea la pregunta de cómo vamos a imprimir cosas. Así que voy a hacer este pequeño tipo temporal imprímeme aquí. Voy a decir que quiero imprimir el registro de componentes. Y en realidad hay una característica agradable del patio de juegos de TypeScript y otros editores de TypeScript llamada a slash assertions. Puedes obtener una extensión de VS Code para hacer esto. Pero si escribes un comentario que tiene este pequeño signo de intercalación y un signo de interrogación, te pedirá que imprima en la pantalla lo que habrías obtenido si hubieras pasado el ratón por allí. Así que aquí obtuvimos el tipo imprímeme como el tipo de registro de componentes. Y se imprimió,
2. Explorando la representación de etiquetas y JSX en TypeScript
Voy a cambiar a destellos y el encabezado es H1 con hijos. Quiero poder obtener un nombre de etiqueta y renderizar el contenido bajo esa etiqueta. TypeScript tiene una característica llamada tipo restringido para asegurarnos de que solo estamos pasando una de las claves reales. Voy a extraer eso a un tipo de componente para que cada vez que diga componente, lo que realmente quiero decir es una de las claves del registro. Quiero poder tener JSX y etiquetas de cierre automático, lo cual no es compatible actualmente.
obtuvimos la versión en cadena de eso. El emoji es corazón brillante. En realidad, voy a cambiar a destellos, un poco menos deslumbrantes, y el encabezado es H1 con hijos. ¡Hurra! Imprimiendo en el sistema de tipos Imprimiendo en el sistema de tipos. Pero necesito hacer más que eso. Quiero poder, digamos, obtener un nombre de etiqueta en particular y renderizar el contenido bajo esa etiqueta. Y puedes hacer eso en TypeScript con esta búsqueda de firma de índice, este pequeño tipo de matriz. Aquí estamos diciendo dame bajo el registro de componentes el tipo bajo el nombre de propiedad emoji, que en este caso, sí es corazones brillantes, perdón, destellos. Si cambiáramos eso a encabezado, obtendríamos H1 hijos slash H1. Si cambiáramos eso a, no sé, ASDF WAT, alguna tontería, obtendríamos la propiedad Red Squiggly's ASDF WAT no existe en el tipo, registro de componentes. Genial. Y esa pequeña restricción allí, el hecho de que necesita ser una de las claves reales del tipo es útil porque quiero escribir un tipo de render que toma un parámetro de tipo y me devuelve el registro de componentes de ese parámetro de tipo, haciendo una versión dinámica de lo que estoy haciendo aquí. Si quisiera renderizar emoji, este dato curioso en realidad funciona. Esto es lo que se llama un tipo genérico o tipo con un parámetro de tipo. Es algo así como una función en el sistema de tipos. Tomamos una etiqueta, digamos emoji, la cadena, y luego hacemos algo. Creamos un nuevo tipo con esa etiqueta. Aquí estamos haciendo el registro de componentes de la etiqueta como resultado, pero estamos obteniendo la queja de que el tipo de etiqueta no puede ser usado para indexar el tipo de registro de componentes. De hecho, si cambiamos a la pestaña de errores aquí, podemos ver eso. Bueno, eso tiene sentido porque ¿qué pasaría si pasara asdf? Necesitamos alguna forma de asegurarnos de que estamos pasando solo una de las claves reales de este tipo. Y TypeScript tiene una característica para eso, se llama un tipo restringido. Con la palabra clave extends, podemos extender la clave del registro de componentes. Voila, no más líneas rojas. Print me es felizmente el emoji brillante, y aquí estamos diciendo que la etiqueta se extiende o debe ser una de las claves del registro de componentes. En realidad, voy a extraer eso a un tipo de componente aquí para que cada vez que diga componente, lo que realmente quiero decir es una de las claves del registro. Si cambiáramos esto a encabezado, sí, renderiza el encabezado muy bien. Renderizando, es un comienzo. Pero quiero poder hacer más que solo tomar un nombre de etiqueta y renderizar bajo la etiqueta. Quiero poder tener JSX. Quiero mis pequeñas etiquetas de cierre automático. Quiero poder hacer algo como emojis de cierre automático, que ahora mismo no es compatible.
3. Representación de JSX en el sistema de tipos
Para extraer algo del tipo proporcionado, utilizamos un tipo inferido condicional. Podemos llegar al tipo proporcionado, agarrar la etiqueta y devolverla. También podemos usar cadenas de texto literales en el sistema de tipos para inferir la etiqueta. Al usar la palabra clave extends, nos aseguramos de que la etiqueta coincida con el registro de componentes. Esto nos permite renderizar un elemento JSX en el sistema de tipos.
Para hacer eso, necesito poder llegar al tipo proporcionado y extraer algo. En este caso, el subconjunto de emoji. Y la forma de hacerlo es con algo llamado un tipo inferido condicional.
Voy a escribir rápidamente eso dado un render, que tal vez, tal vez vamos a empezar con objetos. Si el objeto proporcionado coincide con alguna plantilla, si raw se extiende, coincidentemente la misma palabra clave extends, texto, llamemos a esto cadena, entonces sí, de lo contrario, no. Aquí vemos que este tipo condicional está de hecho diciendo que raw se extiende a este tipo de objeto texto cadena. Sí, no. Eso es un tipo condicional.
Ahora podemos agregar en esto infer tag. Así que llamemos a esto etiqueta en realidad. Etiqueta. ¡Aja! Ahora, estamos llegando al tipo proporcionado raw, extrayendo la etiqueta, y luego devolviéndola un poco como de nuevo, una función del sistema de tipos. Pero somos capaces de hacer un poco de lógica casi aquí. Estamos comprobando si se cumple esta condición. Y esto es un ternario al igual que tendrías un signo de interrogación y dos puntos en el espacio runtime. Pero mencioné antes, no quería formas de objeto, sino formas de cadena. Así que quiero poder hacer esto. Etiqueta de cierre automático. Y puedes hacer eso, de hecho. Puedes tener literales cadenas de texto en el sistema de tipos, y puedes inferir dentro de ellas. Puedes decir, ¿no coincide con este patrón? Inferir etiqueta. Si es así, devuélveme la etiqueta. Emoji. Pero eso es solo la etiqueta. Así que lo que realmente quiero hacer es obtener el registro de componentes de la etiqueta. Y ahora obtenemos esa misma queja de antes. El tipo de etiqueta no puede ser usado para indexar el registro de componentes, lo que significa que de nuevo tengo que usar la palabra clave extends para decir, solo coincide si la etiqueta se extiende a componentes. Y voila. Somos capaces de renderizar un elemento JSX
4. Representación de JSX en el sistema de tipos (Parte 2)
Para resumir, verificamos si el raw se extiende a una plantilla de cadena. Si es así, devolvemos el contenido bajo el registro de esa etiqueta. Podemos usar cadenas de texto literales para agregar antes y después de la etiqueta. Podemos renderizar varias etiquetas de forma recursiva. En lugar de renderizar inválido, renderizamos la entrada original. La representación recursiva de descendientes permite más que solo etiquetas de cierre automático.
en el sistema de tipos. Para resumir antes de avanzar y ponernos más sofisticados, estamos comprobando, ¿este raw, y voy a decir que siempre debería ser una cadena. ¿Se extiende este raw a una plantilla de cadena como la siguiente? Comienza, alguna etiqueta que es un componente. Cerrar. Si es así, devuélveme el contenido bajo el registro de esa etiqueta. De lo contrario, voy a llamar a esto inválido para que quede claro. Yay, estamos a mitad de camino. Pero quiero más. Quiero poder, digamos, imprimir hola mundo antes del emoji, que ahora mismo no está soportado, porque esto no coincide estrictamente con la etiqueta de cierre automático del emoji. Afortunadamente, puedo usar estas cadenas de texto literales en mis resultados. Puedo agregar en otro infer antes, e imprimir antes, y eso funciona. Y puedo hacer infer después, que en este caso es una cadena vacía. Digamos que queremos un signo de exclamación allí o algo así. Ya no necesito esa pintura. Vamos a desplazarnos un poco hacia abajo. Es hermoso. Hola mundo emoji. Eso funciona. Pero, ¿qué pasa si tenemos varios emojis? Digamos que quiero renderizar tres destellos. Bueno, no lo hace ahora mismo, porque cuando inferimos el después, estamos directamente colocándolo de nuevo como una cadena. Necesitamos renderizar de forma recursiva. Necesitamos tomar lo que sea que esté después del registro del componente, en la etiqueta, y devolverlo. Necesitamos volver a ejecutar el renderizado en él, renderizando de forma recursiva hasta que no quede nada más, lo cual realmente funciona. Así que eso es genial. Pero luego obtenemos inválido al final, lo cual creo que tiene sentido, porque una vez que hemos terminado de renderizar la última etiqueta de cierre automático, lo que nos queda es la cadena vacía, que no coincide con esta plantilla. Así que en lugar de renderizar inválido, podemos renderizar lo que nos dieron para empezar. Ahí está. Representación recursiva de descendientes. Pero quiero más. Siempre más. Quiero
5. Representación de Encabezados y Descendientes
Quiero un encabezado. Si raw no se extiende desde la plantilla de cierre automático, pero coincide con una plantilla para la etiqueta de descendientes, entonces renderizamos el encabezado con descendientes. Escribo una utilidad llamada 'render component' para renderizar los descendientes en algún lugar a lo largo de ComponentRegistry. Funciona porque raw coincide con la plantilla que contiene descendientes.
no solo etiquetas de cierre automático. Quiero un encabezado. Quiero encabezado, hola mundo. Dejemos de usar estos emojis tanto. Hagamos un encabezado así. Ahí vamos. Sí, esto no está funcionando. Esto no está detectando. Así que voy a necesitar agregar otra condición.
Que, dato curioso, se te permite hacer. Voy a tener que decir si raw no se extiende desde la plantilla de cierre automático, bueno, ¿y si se extiende desde una plantilla diferente? En este caso, para la etiqueta de descendientes. Así que es lo mismo que antes. Estamos agregando otro caso en el que si no coincidió con la plantilla de cierre automático, pero sí coincide con una plantilla para, veamos aquí, inicio de etiqueta, algunos descendientes inferidos, y etiqueta de cierre, entonces hacemos render, y funciona. Vemos un encabezado renderizado justo aquí. Encabezado, y luego todavía recursamos en el después. Así que obtenemos el segundo, el destello. Es increíble, pero quiero descendientes, lo que significa que voy a seguir adelante y escribir una utilidad aquí, porque quiero renderizar los descendientes en algún lugar a lo largo de ComponentRegistry.
Así que voy a escribir el tipo render component, que toma la etiqueta se extiende a componentes, de nuevo, algo así como una función en el sistema de tipos. Y luego también voy a tomar descendientes se extiende a cadena. Y voy a comprobar, ¿se extiende el registro de componentes de la etiqueta a alguna plantilla que tiene antes este indicador de que quiero descendientes, luego infiero después si lo hace, entonces el resultado será antes los descendientes y después. De lo contrario, vamos a devolver directamente el registro de componentes de la etiqueta, como si nada hubiera pasado. Y voy a usar eso aquí. Render components de la etiqueta. Ooh, y de los descendientes, y ahí vamos. Y voilà, funciona. Encabezado, que se convierte en el H1 y renderiza el Hola Mundo. Si yo fuera a ya-ya-ya, ya-ya-ya, funciona. Y funciona porque raw coincide con esta plantilla con descendientes, lo que significa que luego ejecutamos render component y el registro de componentes de, en este caso, encabezado, de hecho coincide con la plantilla que contiene descendientes. Así que efectivamente reemplazamos la cadena de los descendientes con los descendientes. Así que eso me hace feliz. Pero un poco más de trabajo, estamos viendo que los descendientes incluyen la etiqueta emoji raw, pero en realidad quería renderizar eso.
6. Renderización de Componentes y Recapitulación
En la renderización de componentes, voy a cambiar para renderizar hijos en lugar de devolver directamente el registro de componentes. Usaré el mismo tipo genérico de renderización de componentes, predeterminando hijos a la cadena vacía. Esta es una implementación funcional de un subconjunto limitado del motor de renderización JSX de React en el sistema de tipos. La función de renderización verifica si el texto coincide con plantillas específicas y renderiza en consecuencia.
Entonces, en la renderización de componentes, voy a cambiar. En lugar de devolver directamente el registro de componentes, voy a hacer renderizar hijos, y eso funciona. Hola Mundo brilla. Ah, supongo que siempre podríamos renderizar hijos en caso de que los componentes se refieran entre sí, pero eso está un poco fuera del alcance de esta masterclass de 20 minutos. Así que eso está bien. Y eso está funcionando bastante bien. Solo voy a hacer un poco de limpieza de código. Me gustaría usar el mismo tipo genérico de renderización de componentes aquí, pero en el caso de la etiqueta de cierre automático es una cadena vacía para los hijos, lo que significa que puedo llamar a una característica de código tipado tipos predeterminados para decir en mi renderización de componentes si no tengo hijos, simplemente los predetermino a la cadena vacía. Haciendo este código aquí, creo que es un poco más limpio.
Y ahí lo tienen, una implementación funcional de un subconjunto muy limitado del motor de renderización JSX de React en el sistema de tipos. Repasemos desde el principio antes de terminar. He declarado aquí un tipo de registro de componentes, que es el concepto de un objeto que tiene dos propiedades. Un emoji tiene la cadena de emoji de brillo como su valor. No cualquier cadena, sino específicamente esa cadena. Y también encabezado, que es h1, este indicador de que tenemos hijos, /h1. Luego declaré el tipo de utilidad de componentes aquí, que es cualquiera de las claves del registro de componentes, prácticamente hablando de emoji y encabezado. Luego declaro este tipo de renderización de componentes, que es un tipo genérico. Toma parámetros de tipo y devuelve un tipo resultante basado en eso. Toma uno de los componentes, así que una etiqueta. Y luego hijos, que es una cadena que se predetermina a la cadena vacía. Comprueba. ¿Se extiende el valor del componente bajo esa etiqueta desde esta plantilla? Así que texto antes, el indicador de hijos, texto después. Si se extiende desde esa plantilla, entonces renderizas, o devuelves un resultado de antes, los hijos renderizados, y después, de lo contrario devuelves el componente en sí. Eso se usa en nuestro principal, nuestro conductor, nuestro tipo de trabajador, render, que toma un texto en bruto y verifica dos condiciones. Si el texto es esta plantilla, cualquier cadena antes, la etiqueta del componente dentro de un área de cierre automático, y después, entonces resultamos en el texto anterior, la renderización del componente, la renderización del después. De lo contrario, si es un componente con una plantilla ligeramente diferente, si es antes de la lección signo, el componente en sí con algunos hijos y luego el componente de nuevo con algo después. De manera similar, renderizamos antes. Renderizar componente, etiqueta hijos. Renderizar después. Si el texto en bruto no coincidía con ninguna de esas dos plantillas, lo devolvemos directamente.
7. Conclusión y Recursos Adicionales
Y al final, imprímeme es el resultado. Me siento muy genial y fuerte haciendo eso. Me trae mucha alegría jugar con TypeScript. Esto no es algo que necesites usar en una base diaria de TypeScript, es solo algo genial para explorar. Si quieres aprender más, typescriptblind.org y theplaygrounds/play son fantásticos. También tengo un libro, Aprendiendo TypeScript, y recomiendo definitelytyped y desafíos de tipo como recursos. Cualquier cosa que puedas enviarme me ayudaría a trabajar para todos ustedes. Puedes encontrarme en línea como JoshuakGoldberg.
Y al final, imprímeme es el resultado, donde tomamos este texto, recursamos un par de veces gratis, y obtenemos nuestros resultados de cadena. Yay. Me siento muy genial y fuerte haciendo eso. Y esto me hace feliz. Me trae mucha alegría jugar con TypeScript de esta manera. Pero entiendo totalmente si esto no es para ti, si nunca has jugado con TypeScript antes o hecho cosas profundas en el sistema de tipos, eso está totalmente bien.
De nuevo, esto no es algo que necesites usar en una base diaria de TypeScript, es solo algo genial que creo que es divertido explorar con algunas de las técnicas del sistema de tipos y fundamentos. Si quieres empezar con TypeScript, tengo la mayoría de los conceptos que necesitas para esto en una carpeta diferente en la misma fuente de repositorio slash foundations.
Entonces eso fue divertido. Vamos a concluir. Hablemos un poco de otras cosas por ahora. Si quieres aprender más, typescriptblind.org y theplaygrounds slash play son fantásticos. Son de código abierto en línea. Lo recomiendo mucho. También mencioné que tengo un libro, Aprendiendo TypeScript, que puedes probar absolutamente. Su sitio web tiene muchos artículos y proyectos de código abierto gratuitos para ayudarte a profundizar tu comprensión de TypeScript. Y si los libros no son para ti, o si solo quieres más o otro recurso, realmente me gusta mattpocockstotaltypescript.com. Si te preguntas, esto es todo genial, ¿qué diablos voy a hacer con ello? Bueno, un par de lugares a los que puedes ir después de esto, definitelytyped es uno de los más grandes públicos repositorios de código abierto en el mundo. Es el lugar donde todos los tipos de terceros, así que los paquetes que no describen cómo se ven a los TypeScripts, puedes describir cómo se ven en este repositorio existen. Entonces, si quieres decir, mira el tipo de React, porque React tiene tipos definitivamente tipados puedes ir a definitelytyped, tipos, react, index DTS, y ver un montón de sintaxis que en realidad usa muchas de las características con las que jugamos hoy. Entonces, si alguna vez tenemos un problema con un tipo de terceros, o hay un paquete que usas que no tiene información de tipo, definitely typed es un lugar al que puedes ir para avanzar en eso, para hacerlo mejor para todos. En segundo lugar, los desafíos de tipo es un gran repositorio. También, tienen una página de inicio muy linda, desafío de tipo que enlaza con el GitHub. Tiene un montón de desafíos de tipo pequeños, que se vuelven mucho más complejos para flexionar los conceptos que mostramos hoy. El sistema de tipos es increíblemente poderoso y podría modelar no solo React sino TypeScript en sí mismo. Las personas han implementado TypeScript en el sistema de tipos de TypeScript. Si quieres ser una de esas personas ridículas, esta es una forma de llegar allí. O puedes usar los desafíos fáciles y medios para profundizar tus características básicas de TypeScript y fundamentales del sistema de tipos de TypeScript. Todo lo que hemos hablado hoy es, al menos, construido hacia mi libro Aprendiendo TypeScript. Muy recomendado. Creo que es bastante bueno. Además, porque soy un mantenedor de código abierto a tiempo completo, cualquier cosa que puedas enviarme, en términos de amor, aprecio, dinero, problemas presentados, solicitudes de extracción enviadas me ayudará a trabajar para todos ustedes. De manera similar, TypeScript VS Lint es un proyecto independiente. No estamos asociados con ninguna empresa. Entonces, cualquier cosa que puedas hacer para ayudarnos a presentar errores, enviar código y enviar nos dinero sería realmente apreciado, para ayudarnos a seguir haciendo TypeScript mejor para todos. Dicho todo esto, me lo pasé genial con esto. Espero que lo hayas disfrutado. Puedes encontrarme en línea como JoshuakGoldberg en la mayoría de los sitios, incluyendo por joshuakgoldberg.com. ¡Gracias a todos, disfruten el resto de la masterclass!
Comments