Estaba revisando el archivo .tsconfig de alguien y allí vi --esModuleInterop
Este es su archivo .tsconfig
{ "compilerOptions": { "moduleResolution": "node", "target": "es6", "module": "commonjs", "lib": ["esnext"], "strict": true, "sourceMap": true, "declaration": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "declarationDir": "./dist", "outDir": "./dist", "typeRoots": ["node_modules/@types"] }, "include": ["src/**/*.ts"], "exclude": ["node_modues"] } Aquí, mi pregunta principal es qué es "esModuleInterop": true, y "allowSyntheticDefaultImports": true, . Sé que dependen del "module": "commonjs", . ¿Puede alguien tratar de explicarlo en el mejor lenguaje humano posible?
Los documentos oficiales para los estados allowSyntheticDefaultImports
Permitir importaciones predeterminadas desde módulos sin exportación predeterminada. Esto no afecta la emisión del código, solo la verificación de tipos.
¿Que significa eso? Si no hay ningún valor predeterminado de exportación, creo que el único caso de uso del valor predeterminado de importación sería inicializar algo. ¿Como un soltero?
La siguiente pregunta/respuesta tampoco tiene sentido. ¿Hay alguna manera de usar --esModuleInterop en tsconfig en lugar de que sea una bandera?
Y --esModuleInterop definición en la página del compilador
Emita los ayudantes __importStar y __importDefault para la compatibilidad con el ecosistema babel en tiempo de ejecución y habilite --allowSyntheticDefaultImports para la compatibilidad con el sistema de tipos.
También me pareció difícil de entender/comprender
esModuleInterop genera los ayudantes descritos en los documentos. Mirando el código generado, podemos ver exactamente lo que hacen:
//ts import React from 'react' //js var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var react_1 = __importDefault(require("react")); __importDefault : si el módulo no es un módulo es, lo que devuelve require se convierte en el valor predeterminado. Esto significa que si usa la importación predeterminada en un módulo commonjs , todo el módulo es en realidad el predeterminado.
__importStar se describe mejor en este PR :
TypeScript trata una importación de espacio de nombres (es decir,
import * as foo from "foo") como equivalente aconst foo = require("foo"). Las cosas son simples aquí, pero no funcionan si el objeto principal que se importa es un primitivo o un valor con firmas de llamada/construcción. ECMAScript básicamente dice que un registro de espacio de nombres es un objeto simple.Babel primero requiere en el módulo y busca una propiedad llamada
__esModule. Si__esModulese establece entrue, el comportamiento es el mismo que el de TypeScript, pero de lo contrario, sintetiza un registro de espacio de nombres donde:
- Todas las propiedades se extraen del módulo requerido y se ponen a disposición como importaciones con nombre.
- El módulo requerido originalmente está disponible como una importación predeterminada.
Entonces obtenemos esto:
// ts import * as React from 'react' // emitted js var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); var React = __importStar(require("react")); allowSyntheticDefaultImports es el compañero de todo esto, establecer esto en falso no cambiará los ayudantes emitidos (ambos seguirán teniendo el mismo aspecto). Pero generará un error de mecanografiado si está utilizando la importación predeterminada para un módulo commonjs. Entonces, esta import React from 'react' generará el error Module '".../node_modules/@types/react/index"' has no default export. si allowSyntheticDefaultImports es false .
El problema ocurre cuando queremos importar el módulo CommonJS al código base del módulo ES6.
Antes de estas banderas, teníamos que importar módulos CommonJS con la importación de estrella ( * as something ):
// node_modules/moment/index.js exports = moment // index.ts file in our app import * as moment from 'moment' moment(); // not compliant with es6 module spec // transpiled js (simplified): const moment = require("moment"); moment(); Podemos ver que * era de alguna manera equivalente a la variable exports . Funcionó bien, pero no cumplía con las especificaciones de los módulos es6. En especificaciones, el registro de espacio de nombres en la importación de estrellas ( moment en nuestro caso) puede ser solo un objeto simple, no invocable ( moment() no está permitido).
Con la bandera esModuleInterop podemos importar módulos CommonJS de acuerdo con las especificaciones de los módulos es6 . Ahora nuestro código de importación se ve así:
// index.ts file in our app import moment from 'moment' moment(); // compliant with es6 module spec // transpiled js with esModuleInterop (simplified): const moment = __importDefault(require('moment')); moment.default(); Funciona y es perfectamente válido con las especificaciones de los módulos es6, porque el moment no es un espacio de nombres de la importación de estrellas, es una importación predeterminada.
pero como funciona? Como puede ver, debido a que hicimos una importación predeterminada, llamamos a la propiedad default en un objeto de moment . Pero no declaramos una propiedad default en el objeto de exports en la biblioteca de momentos. La clave es la función __importDefault . Asigna módulo ( exports ) a la propiedad default para módulos CommonJS:
var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Como puede ver, importamos los módulos es6 tal como están, pero los módulos CommonJS están envueltos en un objeto con la clave default . Esto hace posible importar valores predeterminados en módulos CommonJS.
__importStar hace un trabajo similar: devuelve esModules intactos, pero traduce los módulos CommonJS en módulos con una propiedad default :
// index.ts file in our app import * as moment from 'moment' // transpiled js with esModuleInterop (simplified): const moment = __importStar(require("moment")); // note that "moment" is now uncallable - ts will report error! var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; ¿Y qué pasa con allowSyntheticDefaultImports ? ¿Para qué sirve? Ahora los documentos deberían ser claros:
Allow default imports from modules with no default export. This does not affect code emit, just typechecking.
En la escritura de moment , no tenemos una exportación predeterminada especificada, y no deberíamos tenerla, porque solo está disponible con la esModuleInterop . Por lo tanto, allowSyntheticDefaultImports no informará un error si queremos importar el valor predeterminado de un módulo de terceros que no tiene una exportación predeterminada.
En mi caso (estoy usando un proyecto mecanografiado en VS2019 y reacciono v16.13.xx) tuve que reemplazar
import React from 'react';con
declare var require: any var React = require('react');Y funcionó