¿Están equivocados los tipos de módulo?

Rate this content
Bookmark
GithubProject website

¿Alguna vez has instalado una dependencia, la has importado tal como indica el README y te has encontrado inmediatamente con extraños errores de TypeScript sobre importaciones por defecto, el formato de módulo de salida o la interoperabilidad ESM/CommonJS? Cuando todo funciona en tiempo de ejecución pero TypeScript se queja, es posible que te preguntes, ¿es esto un error de TypeScript? O... ¿los tipos están equivocados?


El panorama de los módulos y la publicación de paquetes en JavaScript es complicado. Casi un tercio de los paquetes npm más populares que incluyen tipos tienen algún tipo de problema relacionado con los tipos de módulo que puede causar confusión a los usuarios. Pero las cosas están empezando a mejorar. En esta charla, vamos a:

- explorar cómo llegamos hasta aquí, ¿por qué las cosas son complicadas?
- desarrollar un modelo mental simple y preciso de cómo TypeScript ve los paquetes
- ver cómo la herramienta `arethetypeswrong` puede ayudar a detectar problemas antes de la publicación
- analizar las tendencias en los datos de `arethetypeswrong` para paquetes populares a lo largo del tiempo
- discutir los próximos pasos para mejorar el ecosistema de paquetes con tipos

Andrew Branch
Andrew Branch
30 min
21 Sep, 2023

Video Summary and Transcription

Esta charla analiza las complejidades y terminología del sistema de módulos, las discrepancias entre TypeScript y Node, y cómo solucionar los errores de TypeScript con el paquete Helmet. También explora la resolución de módulos y las discrepancias de tipos, los desafíos con el soporte de TypeScript y Node, y la necesidad de mejoras en paquetes populares.

Available in English

1. Introducción a los módulos y terminología

Short description:

Hola a todos. Mi nombre es Andrew y he estado en el equipo de TypeScript durante los últimos cuatro años. Hoy hablaremos sobre las complejidades del sistema de módulos y la terminología asociada, como ESM y CommonJS. Es importante estar familiarizado con las opciones de resolución de módulos y las diferencias entre Node.16 y Node.Next.

Muy bien. Hola a todos. Y gracias por unirse a mí para hablar sobre módulos. Mi nombre es Andrew. He estado en el equipo de TypeScript durante los últimos cuatro años, y tenemos mucho de qué hablar hoy en poco tiempo, así que eso es suficiente sobre mí. Y creo que todos sabemos que las complejidades del sistema de módulos son súper interesantes y divertidas, así que no hay tiempo para convencerte, pero si viste la charla de Mark Erickson y no tuviste suficiente, esta es definitivamente la charla para ti. Antes de comenzar, hay un poco de terminología que debemos cubrir. Voy a usar el término ESM o el acrónimo ESM alrededor de cien veces en esta charla. Eso significa Módulo ECMAScript. ESM es el sistema de módulos que se agregó a la especificación del lenguaje JavaScript en 2015. Y puedes reconocerlo por las palabras clave import y export. El otro sistema de módulos que discutiremos es CommonJS o CJS. Ese es el estándar de la comunidad que fue popularizado por Node antes de que existiera ESM. Si alguna vez has visto require y module.exports, eso es CommonJS y aún es compatible con Node. También tendré que mencionar algunas extensiones de archivo durante esta charla. Y como pueden superponerse con otros acrónimos, intentaré recordar pronunciarlos con un punto al principio. Será algo útil si estás al menos vagamente familiarizado con algunas opciones de resolución de módulos y TypeScript. Node.16 y Node.Next son actualmente idénticos, así que puedo llamarlos por cualquiera de los dos nombres. Son la única buena opción para Node moderno y no solo significa emitir módulos ES. Esa es una idea equivocada común. Históricamente, el que se llamaba Node era el que todos usaban para casi todo, pero realmente

2. Entendiendo el error de TypeScript con Helmet

Short description:

Vamos a analizar un confuso error de TypeScript con la dependencia de NPM Helmet. Cuando se compila a CommonJS, todo funciona bien. Sin embargo, al cambiar a módulos ES, se produce un error en el que helmet ya no se puede llamar. Vamos a investigar más a fondo ejecutando el código.

Helmet no se ha mantenido al día con las características modernas. Vamos a echar un vistazo a un error de TypeScript un tanto confuso aquí. Estoy mirando la dependencia de NPM llamada Helmet. Acabo de copiar y pegar el ejemplo del archivo README aquí. Esto se compila con TSC y se ejecuta en Node sin problemas. Puedes ver que actualmente estoy compilando a CommonJS. Si quiero cambiar a módulos ES aquí, puedo hacerlo agregando el campo type module a mi propio package JSON para el proyecto. Pero cuando hago eso y vuelvo aquí, ahora obtengo este confuso error de TypeScript que dice que helmet ya no se puede llamar. Si juego lo suficiente con esto, veré TypeScript

3. Entendiendo la Discrepancia entre TypeScript y Node

Short description:

Esta parte discute la discrepancia entre TypeScript y Node al importar el paquete Helmet. Explica cómo TypeScript y Node determinan el formato de archivo y las relaciones entre diferentes tipos de archivos en JavaScript. También explora qué sucede al exportar e importar código transpilado de ESM a CommonJS.

Estoy ejecutando esto. Esto no se ve bien para mí, pero vamos a ejecutarlo y ver qué sucede. Sí, esto se bloquea en Node. Por otro lado, si volvemos a la forma en que estaba donde typescript se queja, pero ejecutamos esto, funciona perfectamente. Parece haber una discrepancia aquí entre typescript y Node, y no podemos satisfacer a ambos al mismo tiempo. Al ver esto, es razonable preguntarse si esto es un error de typescript o si los tipos están mal. Para determinar eso por nosotros mismos, primero necesitamos aprender algunas cosas de antecedentes. La primera es cómo typescript y Node saben qué archivo estamos mencionando cuando decimos importar helmet o require helmet. Será más fácil de explicar esto con el campo de exportación y el archivo package JSON cuando volvamos al código, así que dejémoslo por un momento. La segunda es la detección del formato del módulo. Creo que mencioné que Node admite tanto ESM como CommonJS, por lo que necesita una forma de determinar qué archivo es de qué formato, y lo hace puramente por la extensión del archivo. Un archivo .mjs siempre será ESM, un archivo .cjs siempre será CommonJS, y un archivo .js, tenemos que consultar el package JSON más cercano y si tiene el campo especial type module, será ESM, de lo contrario, CommonJS. Muy bien. La tercera cosa son las relaciones entre los diferentes tipos de archivos con los que trata JavaScript. Cuando ejecutas TSC en un archivo input.ts, obtienes un archivo output.js y un archivo output.d.ts, que se llama archivo de declaración. Cuando publicas código para que otras personas lo consuman, generalmente quieres enviarles el código JavaScript compilado sin procesar. De esa manera, su tiempo de ejecución no necesita otro paso de compilación antes de ejecutarlo. Pero luego si ese usuario también está usando TypeScript ellos mismos, o simplemente un editor con funciones de lenguaje inteligente impulsadas por TypeScript, quieres darles este archivo de declaración, que contiene todo lo que necesita saber sobre el archivo JavaScript, incluyendo los tipos que se han eliminado del archivo original de TypeScript. Debido a que estos dos resultados se producen juntos al mismo tiempo, cuando el compilador o editor del usuario ve un archivo de declaración, um, en realidad ni siquiera necesita ir a buscar y asegurarse de que el archivo JS existe. Debido a esta relación, simplemente puede asumir que existe, saber todo lo que necesita saber sobre la estructura y los tipos, y también la extensión del archivo. Cuando ve .d.ts, sabe que debe haber un archivo .js allí. Y lo mismo ocurre con las extensiones de archivo específicas del formato que discutimos anteriormente. Un archivo .mts produce un archivo .mjs, y obtenemos un archivo .d.mts junto a eso. Y luego tenemos un análogo para las versiones de CommonJS aquí. Puede parecer que estoy enfatizando un punto relativamente simple aquí, pero te sorprendería la cantidad de problemas que tienen como causa raíz el romper esta relación de alguna manera.

Lo siguiente que necesitamos aprender es qué sucede si intentamos compilar esta exportación predeterminada a CommonJS. Las exportaciones predeterminadas en ESM son solo una forma especial de importación con nombre con una sintaxis especial adjunta. Entonces, lo único que haremos es adjuntar una asignación de propiedad con nombre aquí en module.exports con el valor que estamos exportando. Y luego también definiremos esta bandera aquí que simplemente dice, hey, he sido transpilado de ESM a CommonJS. Nuestro archivo de declaración, por otro lado, conserva esta sintaxis de ESM. Bien, casi terminado aquí. Lo último que necesitamos ver es qué sucede cuando intentamos importar esa misma exportación predeterminada transpilada que acabamos de ver. Entonces aquí tenemos nuestra exportación predeterminada transpilada a CommonJS nuevamente. Y si estamos importando esto en otro archivo que también se está compilando a CommonJS, entonces tiene sentido que lo que deberíamos obtener con una importación predeterminada allí sea el valor que exportamos por defecto.

4. Entendiendo las Importaciones y Exportaciones Predeterminadas en Node

Short description:

La importación predeterminada y la exportación predeterminada deben coincidir. Al importar un módulo CommonJS en Node, se devuelve todo el objeto module.exports. Para diagnosticar el error entre TypeScript y Node, debemos examinar los archivos que están buscando, comenzando con el archivo package.json de Helmet.

La importación predeterminada y la exportación predeterminada deben coincidir. Así que solo debería obtener hola mundo. Y las transformaciones que se aplican al compilar esto desde una importación predeterminada a una declaración require aseguran que eso se cumpla. Por otro lado, cuando hacemos esto en un módulo ES real en node, node no entiende que nuestro módulo de exportación aquí está fingiendo ser ESM. Y en node, cuando importas por defecto un módulo CommonJS, lo que obtendrás siempre es todo el objeto module.exports.

Entonces aquí, puedes ver que he registrado el module.exports y obtengo un objeto con una propiedad llamada default. Y eso es lo mismo que obtendría en node si hiciera una importación predeterminada real de esta falsa exportación predeterminada. Obtendría la propiedad module.exports con este default. Bien, ahora deberíamos saber lo suficiente como para poder diagnosticar este error por nosotros mismos. Si queremos entender la diferencia entre TypeScript y node, necesitamos saber qué archivos están buscando cada uno de ellos.

Entonces, primero, veamos el archivo package.json de Helmet. Bien, tenemos este campo de exportaciones nuevamente que mencionamos brevemente antes. Lo que vamos a hacer aquí es echar un vistazo a esta clave de punto. Y esto coincide con la ruta. Entonces, como estoy importando Helmet y no como Helmet slash utils, la ruta de punto coincide y entraremos aquí y veremos algunas condiciones. Así que vamos a fingir que estamos resolviendo como node primero. Estamos resolviendo una importación, no un require. Y lo que node va a hacer es mirar cada una de estas condiciones de arriba a abajo y seguir hasta que encuentre una que coincida. Entonces, la importación coincide porque estamos resolviendo una importación. Así que entraremos en este objeto y veremos algunas condiciones anidadas. Los tipos no coinciden. No es una de las condiciones que node establece aquí. Así que lo omitirá y comprobará el valor predeterminado. El valor predeterminado siempre coincide con todo. Entonces, node se resolverá a index dot MJS. Ahora, si estamos resolviendo como TypeScript, hacemos exactamente el mismo proceso con una excepción. TypeScript siempre coincide con la condición de tipos. Por lo general, no es necesario establecer la condición de tipos a menos que coloques todos tus archivos de declaración en un directorio completamente separado del JavaScript. Pero está aquí y coincide. Entonces, TypeScript se resolverá a index dot D dot TS.

5. Resolviendo la Discrepancia de Formato y Arreglando los Tipos

Short description:

Por cierto, hay una bandera de línea de comandos para TypeScript llamada trace resolution. Esto mostrará todo lo que está sucediendo. Ahora que sabemos qué archivos están en juego, hablemos del formato por un momento. Tenemos una discrepancia de formato. Si TypeScript supiera que este archivo está intentando tipar un módulo ES real, vería esta exportación como helmet por defecto y sabría que cuando lo importemos por defecto, funcionará como esperamos. Pero lo que TypeScript piensa es que este archivo representa un archivo Common JS que hace exports.default igual a helmet. Si soy el autor de la biblioteca, parece que todo está funcionando en tiempo de ejecución y solo necesito cambiar algo en la configuración de los tipos para solucionar este problema. El autor de la biblioteca Helmet ya ha solucionado este problema y lo ha resuelto mucho antes de que comenzara esta charla, por eso me siento cómodo analizándolo un poco en una conferencia. En lugar de repetir el trabajo que hizo y arreglar los tipos para que coincidan con el JavaScript existente, podría ser más divertido e instructivo para nuestros propósitos intentar romper el JavaScript existente para que coincida con los tipos existentes. La forma en que lo haré es comenzar haciendo que las extensiones coincidan. Sabemos que el archivo index.d.ts representa un archivo .js, así que eliminaré la M aquí. Y nuestra demostración no va a tocar el lado de require, pero haré que coincida también. Bien, el index.js que acabamos de poner aquí en realidad aún no existe. Así que lo crearé. Y como dijimos antes, sabemos que este archivo debe ser Common JS. Así que comenzaremos copiando el contenido de index.cjs aquí y asumiremos que está bastante cerca y luego lo comprobaremos. Vimos que teníamos esa exportación helmet por defecto, que vemos representada aquí como exports.default igual a helmet. Así que esto se ve bien. Creo que el único problema aquí es que esta línea está sobrescribiendo eso.

Por cierto, hay una bandera de línea de comandos para TypeScript llamada trace resolution. Esto mostrará todo lo que está sucediendo. Así que no tienes que recordar esto si solo quieres ver a qué está resolviendo TypeScript.

Ahora que sabemos qué archivos están en juego, hablemos del formato por un momento. Sabemos que node en este archivo .js lo interpretará como ESM debido a la extensión de archivo. El archivo .d.ts, por otro lado, sabemos que representa un archivo .js debido al triángulo que vimos antes en los tipos de archivo de TypeScript. Y el formato de ese archivo .js se determinaría por el campo module en el archivo package.json, pero no hay ninguno aquí en este package.json en ningún lugar. Así que sabemos que ese archivo JavaScript debe ser Common JS. Aquí tenemos una discrepancia de formato.

El archivo index.mjs de node sabemos que debe ser ESM y el archivo de declaración de tipos sabemos que representa un archivo Common JS. Y si echamos un vistazo a los propios tipos, nos preocupa esta parte de la exportación helmet por defecto. Y esto termina de explicar lo que estábamos viendo. Si TypeScript supiera que este archivo está intentando tipar un módulo ES real, vería esta exportación como helmet por defecto y sabría que cuando lo importemos por defecto, funcionará como esperamos. Sabes, tenemos una exportación por defecto. La importamos por defecto y la podemos usar sin problemas. Pero lo que TypeScript piensa es que este archivo representa un archivo Common JS que hace exports.default igual a helmet. Y si ese es el caso, cuando lo importemos por defecto en Node, todavía necesitaremos hacer ese .default adicional para acceder a la propiedad exports.default ya que solo estamos obteniendo la vista de module.exports en su totalidad.

Entonces, si soy el autor de la biblioteca, parece que todo está funcionando en tiempo de ejecución y solo necesito cambiar algo en la configuración de los tipos para solucionar este problema. Pero para revelar un pequeño spoiler, el autor de la biblioteca Helmet ya ha solucionado este problema y lo ha resuelto mucho antes de que comenzara esta charla, por eso me siento cómodo analizándolo un poco en una conferencia. Así que en lugar de repetir el trabajo que hizo y arreglar los tipos para que coincidan con el JavaScript existente, podría ser más divertido e instructivo para nuestros propósitos intentar romper el JavaScript existente para que coincida con los tipos existentes. La forma en que lo haré es comenzar haciendo que las extensiones coincidan. Sabemos que el archivo index.d.ts representa un archivo .js, así que eliminaré la M aquí. Y nuestra demostración no va a tocar el lado de require, pero haré que coincida también. Bien, el index.js que acabamos de poner aquí en realidad aún no existe. Así que lo crearé. Y como dijimos antes, sabemos que este archivo debe ser Common JS. Así que comenzaremos copiando el contenido de index.cjs aquí y asumiremos que está bastante cerca y luego lo comprobaremos. Vimos que teníamos esa exportación helmet por defecto, que vemos representada aquí como exports.default igual a helmet. Así que esto se ve bien. Creo que el único problema aquí es que esta línea está sobrescribiendo eso.

6. Solucionando el Error de TypeScript con Helmet

Short description:

Nos encontramos con un error de TypeScript al usar el paquete Helmet. Al realizar cambios en el código JavaScript, pudimos hacer coincidir los tipos y resolver el error. Este patrón de una biblioteca CommonJS asignando a exports.default es común, y los tipos advierten correctamente a los usuarios que están importando en Node.esm. Para simplificar el proceso de análisis, creé una herramienta llamada rthetypeswrong que verifica la resolución de módulos e identifica las discrepancias entre TypeScript y el tiempo de ejecución. Helmet 7.0 ha resuelto los problemas al tipar correctamente los archivos index.cjs e index.mjs.

Entonces, simplemente vamos a eliminarlo. Y creo que ahora deberíamos tener acuerdo. Volviendo a nuestro archivo de entrada, recordemos que no tocamos los tipos en absoluto. Aún esperamos ver este error de TypeScript, pero lo que deberíamos ver es que si TypeScript nos está dando un error, Node también se va a bloquear. Así que vamos a construir y ejecutar eso. Y sí, esta vez, helmet no es una función. Y si en cambio hacemos lo que TypeScript nos está diciendo que deberíamos hacer, helmet.default, y luego construimos y ejecutamos esto, esto ahora funciona.

Ok, ¿por qué pasamos por este tipo de ejercicio tonto de hacer un cambio probablemente indeseable en el JavaScript para que coincida con los tipos? Bueno, a veces cuando las personas me presentan un problema como este y les explico lo que está sucediendo en los tipos, dirán algo así como, sabes, parece que TypeScript está siendo demasiado pedante aquí. Claramente podía encontrar los tipos antes, y mi JavaScript de CommonJS y ESM son casi idénticos. Así que si TypeScript puede encontrar el archivo, simplemente, ya sabes, entiende lo que quiero decir y no me hagas pasar por tantos obstáculos. Pero lo que hemos demostrado aquí es que los tipos no están incorrectos per se. No son inválidos. Simplemente describen una biblioteca diferente a la que teníamos aquí. Describen la que acabamos de hacer a través de nuestros cambios. Y este patrón de, esencialmente, una biblioteca CommonJS que asigna a exports.default es bastante común. Es posible que el autor de la biblioteca no haya tenido la intención de hacer esto, pero lo he visto varias veces en la práctica. Y cuando los tipos para eso son correctos, advierte correctamente a los usuarios que están importando en Node.esm que necesitarán de manera no intuitiva, pero legítimamente, este extra.default para acceder a lo que creen que necesitan acceder. Entonces, aunque todos ustedes ahora son expertos en resolución de módulos e interoperabilidad, puede ser demasiado pedir que todos los usuarios o incluso todos los autores de bibliotecas sean expertos en este tema.

Así que hice una herramienta que puede hacer parte de este análisis por ti. rthetypeswrong es una herramienta que descarga un paquete NPM y luego, para cada modo de resolución de módulos admitido por TypeScript, realiza esencialmente el análisis que acabamos de hacer manualmente. Verifica a qué está resolviendo el compilador de TypeScript y a qué está resolviendo el tiempo de ejecución, analiza cada uno de esos archivos y verifica las discrepancias. Aquí estábamos viendo Helmet 6.1.2 y vemos que cuando estamos importando desde ESM en el modo Node 16, resolvemos a tipos de CommonJS pero JavaScript de ESM. Y eso es exactamente lo que vimos. Y mencioné que Helmet ya ha solucionado esto. Así que echemos un vistazo a eso. En Helmet 7.0, no vemos problemas y CommonJS se resuelve a CommonJS y ESM a ESM. Esto se ve genial. Y como no nos molestamos en arreglar los tipos nosotros mismos, podemos ver cómo el autor de la biblioteca solucionó esto. Aquí vemos que tanto los archivos index.cjs como index.mjs ahora están tipados con su propio archivo de declaración con la extensión correcta. Así que TypeScript puede ser consciente de todos los archivos que están allí y conocer los formatos de todo.

7. Analizando la Resolución de Módulos y las Discrepancias de Tipos

Short description:

Ahora hay una versión de línea de comandos de la herramienta, lo que facilita su integración en los procesos de publicación o la verificación de paquetes locales. Probamos los cambios realizados en Helmet utilizando la herramienta y solucionamos las discrepancias entre los tipos y JavaScript. Sin embargo, la versión ESM se resuelve a la versión Common JS, lo cual no es el resultado deseado. Al analizar los paquetes de npm a lo largo del tiempo, se observan mejoras para los usuarios en todos los grupos, excepto para los usuarios de node ESM, que enfrentan errores en más de una cuarta parte de las dependencias de tipos. Los problemas en la lista ESM incluyen declaraciones de CommonJS que intentan representar JavaScript de ES y resoluciones sin tipos. Este último es un problema más grave, ya que TypeScript no puede encontrar las declaraciones de tipos que se envían con el paquete. Estos problemas pueden ser relativamente fáciles de solucionar, a diferencia de otros que requieren ajustes en los sistemas de compilación. Los problemas surgen de la adopción rápida de la sintaxis ESM antes de que los implementadores y TypeScript se pusieran al día, lo que llevó a suposiciones incorrectas sobre la interoperabilidad de los dos formatos. Las soluciones alternativas para la falta de soporte de TypeScript también se convirtieron en problemas cuando TypeScript introdujo los modos Node-16 y Node-next. Es importante tener en cuenta que cuando se consideran incorrectos los tipos de un paquete, no se trata de un juicio sobre las publicaciones anteriores.

También hay una versión de línea de comandos de esta herramienta ahora. Por lo tanto, es fácil integrarla en su proceso de publicación o verificar un paquete local. Por diversión, probemos eso en los cambios que hicimos en Helmet. Ejecutaré attw y luego pack, y lo ejecutaremos en node-module-slash-helmet. Puedes ver aquí que hemos solucionado las discrepancias, no se encontraron problemas entre los tipos y JavaScript, pero a diferencia de la versión anterior, a diferencia de en Helmet 7, podemos ver que la versión ESM de las cosas se resuelve a la versión Common JS de la biblioteca. Eso no es un problema, pero no es la solución que probablemente Helmet quería hacer, que era, ya sabes, hacer que ESM se resuelva al ESM que ya está allí.

Lo bueno de hacer todo este análisis en los paquetes npm es que nos brinda una forma natural de ver muchos datos a lo largo del tiempo. Ejecuté rthetypeswrong en los 6,000 principales paquetes npm tal como existían el primero de cada mes desde enero del año pasado, desglosados por algoritmo de resolución de módulos. Podemos ver que las cosas están mejorando para los usuarios en todos los grupos aquí, con la ligera excepción del aumento reciente en node 10, que proviene de paquetes que dejan de admitir esto. Por otro lado, las cosas no se ven bien para los usuarios de node ESM, con más de una cuarta parte de las dependencias de tipos mostrando algún tipo de error. Esto nos pone en una situación difícil, porque si la comunidad va a migrar a ESM, y creo que debería hacerlo, pero de todos modos, ya está sucediendo. Sería muy bueno si pudiéramos animar a los usuarios a hacer esa transición primero antes que las bibliotecas, pero eso es difícil de vender como usuario de TypeScript si hacer el cambio va a romper una cuarta parte de tus dependencias.

Si desglosamos los problemas que estamos viendo en esa lista ESM de problemas aquí, podemos ver un poco más de matices. Esta línea verde es el tipo de problema que acabamos de ver, donde las declaraciones de CommonJS intentan representar JavaScript de ES. Parece que esto está aumentando a largo plazo, pero por otro lado, está intercambiando uno por uno con esta línea naranja, que son resoluciones sin tipos. Esto significa que el paquete estaba enviando declaraciones de tipos, pero TypeScript no pudo encontrarlas en absoluto. Y ese es un problema peor, aunque los números agregados no lo demuestren realmente. No tenemos tiempo para entrar en qué son cada uno de estos problemas que el tipo fuerte puede detectar, pero señalaré que el segundo problema más frecuente aquí, a diferencia de algunos de los otros, creo que son relativamente fáciles de solucionar la mayor parte del tiempo. Algunos de los otros requieren ajustes en los sistemas de compilación, y estos a menudo son solo una corrección de una línea. Tienen que ver con mezclar export equals y export default en archivos de declaración. Entonces, ¿de dónde vienen todos estos problemas en primer lugar? Las lagunas en esta línea de tiempo son la mejor explicación que se me ocurre. Escribir sintaxis ESM se volvió increíblemente popular muy temprano en 2014, antes de que se publicara la especificación final de ESM en 2015. Y esto es normal, probar características del lenguaje temprano es parte del proceso de TC39. Pero lo que vimos fue una adopción muy rápida de escribir código ESM en la comunidad con un largo retraso antes de que los implementadores pudieran ponerse al día. Y luego otro largo retraso después de eso antes de que TypeScript pudiera ponerse al día con Node. En todo este tiempo, ya sea que lo supieran o no, las herramientas que te permiten escribir sintaxis ESM y generar common.js estaban codificando estas suposiciones implícitas sobre cómo iban a interoperar esos dos formatos en tiempo de ejecución real. Y no todas esas suposiciones resultaron ser correctas. Al mismo tiempo, entre 2019 y 2022, cuando los paquetes querían enviar ESM y declaraciones de tipos, tuvieron que idear soluciones alternativas para la falta de soporte de TypeScript aquí. Pero una vez que TypeScript se puso al día y lanzó los modos Node-16 y Node-next, esas soluciones alternativas se convirtieron en problemas en sí mismas. Es importante entender que cuando digo que los tipos son incorrectos para un paquete, no es un juicio sobre lo que se publicó en el pasado.

8. Desafíos con el Soporte de TypeScript y Node

Short description:

Muchos de los problemas que vemos se pueden atribuir al soporte rezagado de TypeScript para Node. Me he estado enfocando en este espacio de problemas durante aproximadamente un año, trabajando en difundir información y mejorando la herramienta ¿Los Tipos están Equivocados?. Ahora, estoy buscando tener un impacto más directo en los problemas existentes en los paquetes populares.

Es solo una observación sobre lo que funciona o no funciona ahora. De hecho, muchos de los problemas que vemos tienen sus raíces en el soporte rezagado de TypeScript para Node. Por lo tanto, este espacio de problemas ha sido mi principal enfoque durante la mayor parte de un año. Hasta ahora, la mayor parte de lo que he estado haciendo ha sido para difundir información desde ¿Los Tipos están Equivocados? hasta la documentación oficial de TypeScript. Creo que esa fase de mi trabajo está llegando a su fin y tengo algunas mejoras en el plan de ¿Los Tipos están Equivocados? de las que estoy emocionado. Pero anticipando un cambio hacia la búsqueda de formas de tener un impacto más directo en algunos de los problemas existentes que vemos en los paquetes populares. Entonces, si ya estás familiarizado con este espacio o simplemente quieres aprender y participar en eso, por favor contáctame y podemos intentar solucionar algunos de estos problemas juntos. Si eres un autor de bibliotecas, te recomiendo encarecidamente que ejecutes ¿Los Tipos están Equivocados?, la CLI, como parte de tu proceso de publicación para que puedas detectar algunos de estos problemas antes que tus usuarios, y si eres un usuario y notas uno de estos errores extraños en una de tus dependencias y ¿Los Tipos están Equivocados? puede detectar que algo está sucediendo aquí, considera abrir un problema contra la biblioteca. Solo recuerda buscar duplicados y proporcionar una reproducción clara y, lo más importante, sé amable con los mantenedores. Te advertí al principio que esta charla iba a ser bastante abstracta y ligera en consejos prácticos. Así que intentaré terminar con algunas recomendaciones concretas. En primer lugar, en mi opinión, es hora de adoptar ESM solo para nuevos proyectos. Al menos, no asumas automáticamente que necesitas enviar CommonJS. Cuando solo produces un formato de módulo y especialmente cuando es ESM y especialmente cuando lo produces con TSC, prácticamente estás garantizado de estar inmune a todos los diferentes tipos de problemas que ¿Los Tipos están Equivocados? puede detectar en las bibliotecas populares. En segundo lugar, si estás interesado en más validación como parte de tu proceso de publicación, recomiendo publint.dev como contraparte de ¿Los Tipos están Equivocados?. Cubre un montón de reglas no relacionadas con TypeScript que puedes verificar para asegurarte de que estás configurando correctamente tu package JSON. Y luego no tuvimos tiempo para entrar en esto, pero creo que hay muchas razones por las que deberías pensarlo dos veces antes de publicar un paquete dual ESM CommonJS. Pero si es necesario, te recomiendo investigar DNT, que es el publicador de paquetes de Deno para Node. Tiene algunas características únicas, como ejecutar tu conjunto de pruebas de Deno tanto en los paquetes de salida CommonJS como ESM. Eso puede darte una mayor confianza, especialmente cuando lo combinas con algo como ¿Los Tipos están Equivocados?, para que si te encuentras con un problema que se está introduciendo en tus paquetes, lo detectarás antes de publicarlo. Por último, este es un tema en el que veo mucha negatividad y sentimientos de impotencia. No puedo negar que estas cosas pueden ser complicadas, pero definitivamente no son más difíciles que aprender un nuevo framework de front-end cada semana o hacer aritmética en el espacio de tipos o centrar verticalmente un div. Veo a todos ustedes haciendo cosas impresionantes y complicadas todos los días, y sugiero que veas este tipo de espacio desordenado como una oportunidad para aprender cosas nuevas o incluso convertirte en un experto en un área que realmente necesita más expertos. Así que, gracias por escuchar y nos vemos en la sesión de preguntas y respuestas y en las salas de discusión.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Vite: Rethinking Frontend Tooling
JSNation Live 2021JSNation Live 2021
31 min
Vite: Rethinking Frontend Tooling
Top Content
Vite is a new build tool that intends to provide a leaner, faster, and more friction-less workflow for building modern web apps. This talk will dive into the project's background, rationale, technical details and design decisions: what problem does it solve, what makes it fast, and how does it fit into the JS tooling landscape.
Scaling Up with Remix and Micro Frontends
Remix Conf Europe 2022Remix Conf Europe 2022
23 min
Scaling Up with Remix and Micro Frontends
Top Content
Do you have a large product built by many teams? Are you struggling to release often? Did your frontend turn into a massive unmaintainable monolith? If, like me, you’ve answered yes to any of those questions, this talk is for you! I’ll show you exactly how you can build a micro frontend architecture with Remix to solve those challenges.
React Compiler - Understanding Idiomatic React (React Forget)
React Advanced Conference 2023React Advanced Conference 2023
33 min
React Compiler - Understanding Idiomatic React (React Forget)
Top Content
React provides a contract to developers- uphold certain rules, and React can efficiently and correctly update the UI. In this talk we'll explore these rules in depth, understanding the reasoning behind them and how they unlock new directions such as automatic memoization. 
Full Stack Components
Remix Conf Europe 2022Remix Conf Europe 2022
37 min
Full Stack Components
Top Content
Remix is a web framework that gives you the simple mental model of a Multi-Page App (MPA) but the power and capabilities of a Single-Page App (SPA). One of the big challenges of SPAs is network management resulting in a great deal of indirection and buggy code. This is especially noticeable in application state which Remix completely eliminates, but it's also an issue in individual components that communicate with a single-purpose backend endpoint (like a combobox search for example).
In this talk, Kent will demonstrate how Remix enables you to build complex UI components that are connected to a backend in the simplest and most powerful way you've ever seen. Leaving you time to chill with your family or whatever else you do for fun.
TypeScript and React: Secrets of a Happy Marriage
React Advanced Conference 2022React Advanced Conference 2022
21 min
TypeScript and React: Secrets of a Happy Marriage
Top Content
TypeScript and React are inseparable. What's the secret to their successful union? Quite a lot of surprisingly strange code. Learn why useRef always feels weird, how to wrangle generics in custom hooks, and how union types can transform your components.
Making JavaScript on WebAssembly Fast
JSNation Live 2021JSNation Live 2021
29 min
Making JavaScript on WebAssembly Fast
Top Content
JavaScript in the browser runs many times faster than it did two decades ago. And that happened because the browser vendors spent that time working on intensive performance optimizations in their JavaScript engines.Because of this optimization work, JavaScript is now running in many places besides the browser. But there are still some environments where the JS engines can’t apply those optimizations in the right way to make things fast.We’re working to solve this, beginning a whole new wave of JavaScript optimization work. We’re improving JavaScript performance for entirely different environments, where different rules apply. And this is possible because of WebAssembly. In this talk, I'll explain how this all works and what's coming next.

Workshops on related topic

React, TypeScript, and TDD
React Advanced Conference 2021React Advanced Conference 2021
174 min
React, TypeScript, and TDD
Top Content
Featured WorkshopFree
Paul Everitt
Paul Everitt
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported.

The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials.

React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.
Best Practices and Advanced TypeScript Tips for React Developers
React Advanced Conference 2022React Advanced Conference 2022
148 min
Best Practices and Advanced TypeScript Tips for React Developers
Top Content
Featured Workshop
Maurice de Beijer
Maurice de Beijer
Are you a React developer trying to get the most benefits from TypeScript? Then this is the workshop for you.In this interactive workshop, we will start at the basics and examine the pros and cons of different ways you can declare React components using TypeScript. After that we will move to more advanced concepts where we will go beyond the strict setting of TypeScript. You will learn when to use types like any, unknown and never. We will explore the use of type predicates, guards and exhaustive checking. You will learn about the built-in mapped types as well as how to create your own new type map utilities. And we will start programming in the TypeScript type system using conditional types and type inferring.
Using CodeMirror to Build a JavaScript Editor with Linting and AutoComplete
React Day Berlin 2022React Day Berlin 2022
86 min
Using CodeMirror to Build a JavaScript Editor with Linting and AutoComplete
Top Content
WorkshopFree
Hussien Khayoon
Kahvi Patel
2 authors
Using a library might seem easy at first glance, but how do you choose the right library? How do you upgrade an existing one? And how do you wade through the documentation to find what you want?
In this workshop, we’ll discuss all these finer points while going through a general example of building a code editor using CodeMirror in React. All while sharing some of the nuances our team learned about using this library and some problems we encountered.
Deep TypeScript Tips & Tricks
Node Congress 2024Node Congress 2024
83 min
Deep TypeScript Tips & Tricks
Top Content
Workshop
Josh Goldberg
Josh Goldberg
TypeScript has a powerful type system with all sorts of fancy features for representing wild and wacky JavaScript states. But the syntax to do so isn't always straightforward, and the error messages aren't always precise in telling you what's wrong. Let's dive into how many of TypeScript's more powerful features really work, what kinds of real-world problems they solve, and how to wrestle the type system into submission so you can write truly excellent TypeScript code.
Testing Web Applications Using Cypress
TestJS Summit - January, 2021TestJS Summit - January, 2021
173 min
Testing Web Applications Using Cypress
WorkshopFree
Gleb Bahmutov
Gleb Bahmutov
This workshop will teach you the basics of writing useful end-to-end tests using Cypress Test Runner.
We will cover writing tests, covering every application feature, structuring tests, intercepting network requests, and setting up the backend data.
Anyone who knows JavaScript programming language and has NPM installed would be able to follow along.
0 to Auth in an Hour Using NodeJS SDK
Node Congress 2023Node Congress 2023
63 min
0 to Auth in an Hour Using NodeJS SDK
WorkshopFree
Asaf Shen
Asaf Shen
Passwordless authentication may seem complex, but it is simple to add it to any app using the right tool.
We will enhance a full-stack JS application (Node.JS backend + React frontend) to authenticate users with OAuth (social login) and One Time Passwords (email), including:- User authentication - Managing user interactions, returning session / refresh JWTs- Session management and validation - Storing the session for subsequent client requests, validating / refreshing sessions
At the end of the workshop, we will also touch on another approach to code authentication using frontend Descope Flows (drag-and-drop workflows), while keeping only session validation in the backend. With this, we will also show how easy it is to enable biometrics and other passwordless authentication methods.
Table of contents- A quick intro to core authentication concepts- Coding- Why passwordless matters
Prerequisites- IDE for your choice- Node 18 or higher