Introdução: Por que esModuleInterop não é apenas uma opção no tsconfig
\n\nSe você já escreveu em TypeScript e importou uma biblioteca como express ou lodash, certamente já se deparou com o momento em que o compilador reclama de import * as express from 'express'. Ou, ao contrário, você usa tranquilamente import express from 'express', mas no código JavaScript gerado vê uma construção estranha module.exports.default. Que mágica é essa? A mágica (ou a falta dela) é a flag esModuleInterop no seu tsconfig.json.
Este parâmetro é uma das perguntas mais populares no Stack Overflow (milhares de visualizações!), e não é surpresa: sem entendê-lo, é fácil cair na armadilha da incompatibilidade de módulos. Neste tutorial, não vamos apenas explicar a teoria, mas percorrer o caminho do problema à solução com exemplos práticos. Vamos lá!
\n\nO problema: Conflito de dois mundos de módulos
\n\nTypeScript é uma extensão do JavaScript, e o JavaScript historicamente tem dois sistemas principais de módulos:
\n- \n
- CommonJS (CJS) — padrão do Node.js, usa
require()emodule.exports. Exemplo:const express = require('express');\n - ES Modules (ESM) — padrão moderno, usa
importeexport. Exemplo:import express from 'express';\n
O problema é que esses sistemas tratam a exportação padrão (default export) de maneiras diferentes. No ESM, um módulo pode ter um export default, e ele é importado como import name from 'module'. No CommonJS, não existe o conceito de "exportação padrão" — existe apenas o objeto module.exports, que pode ser qualquer coisa.
Quando o TypeScript compila seu código para CommonJS (o que é frequentemente necessário para Node.js), ele precisa decidir: como representar import x from 'y' na forma de require()? Sem o esModuleInterop, o TypeScript faz isso literalmente: cria module.exports.default, o que quebra a importação de bibliotecas escritas para CommonJS.
O que é esModuleInterop?
\n\nesModuleInterop é uma flag no tsconfig.json que ativa compatibilidade adicional entre CommonJS e ES Modules. Quando definida como true, o TypeScript gera código auxiliar que permite escrever import foo from 'bar' mesmo para módulos CommonJS que não possuem um default-export.
Por padrão, no TypeScript 4.x e superiores, essa flag é true (se você usar --init), mas em projetos antigos ela era frequentemente desativada. Por quê? Porque ela altera o comportamento da importação e, sem entendê-la, isso leva a bugs.
Como fica no tsconfig.json
\n{\n "compilerOptions": {\n "esModuleInterop": true,\n // outras configurações...\n }\n}\n\n\nAtivar esta flag também ativa automaticamente o allowSyntheticDefaultImports, que permite a importação padrão de módulos sem um default-export real no nível de tipos.
Tutorial passo a passo: do problema à solução
\n\nVamos analisar a situação com um exemplo real. Suponha que temos uma biblioteca simples em CommonJS:
\n\n// myLib.js (CommonJS)\nmodule.exports = function greet(name) {\n return `Olá, ${name}!`;\n};\n\n\nQueremos usá-la em um projeto TypeScript. Vamos criar o arquivo index.ts:
// index.ts\nimport greet from './myLib';\nconsole.log(greet('Mundo'));\n\n\nAgora, vejamos o que acontece ao compilar com diferentes configurações de esModuleInterop.
Passo 1: Sem esModuleInterop (false)
\n\nVamos executar a compilação com "esModuleInterop": false. O TypeScript emitirá um erro:
O módulo '"myLib"' não tem d