[typescript] tsconfig 파일의 esModuleInterop 이해

누군가 .tsconfig파일 을 확인 하고 있었는데 거기서 발견했습니다--esModuleInterop

이것은 그의 .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"]
}

여기서 내 주요 질문은 무엇 "esModuleInterop": true,이며
"allowSyntheticDefaultImports": true,. 나는 그들이 일종의 "module": "commonjs",. 누군가가 가능한 최고의 인간 언어로 설명하려고 할 수 있습니까?

allowSyntheticDefaultImports주를 위한 공식 문서

기본 내보내기없이 모듈에서 기본 가져 오기를 허용합니다. 이것은 코드 방출에 영향을 미치지 않고 단지 유형 검사에만 영향을줍니다.

그게 무슨 뜻입니까? 가져 오기 기본값의 유일한 사용 사례가 무언가를 초기화하는 것이라고 생각하는 것보다 내보내기 기본값이 없다면? 싱글 톤처럼?

다음 질문 / 답변도 의미가 없습니다
. tsconfig에서 플래그가 아니라 –esModuleInterop을 사용하는 방법이 있습니까?

그리고 --esModuleInterop컴파일러 페이지에 정의

런타임 바벨 생태계 호환성을 위해 __importStar 및 __importDefault 헬퍼를 내보내고 유형 시스템 호환성을 위해 –allowSyntheticDefaultImports를 활성화하십시오.

또한 이해 / 이해하기 어려운 것 같았습니다.



답변

문제 설명

CommonJS 모듈을 ES6 모듈 코드베이스로 가져 오려고 할 때 문제가 발생합니다.

이 플래그 이전에는 별표 ( * as something) 가져 오기를 사용 하여 CommonJS 모듈을 가져와야했습니다.

// 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();

우리는 그것이 *어떻게 든 exports변수 와 동등 하다는 것을 알 수 있습니다 . 잘 작동했지만 es6 모듈 사양과 호환되지 않았습니다. 사양에서 스타 가져 오기 ( moment이 경우)의 네임 스페이스 레코드는 호출 할 수없는 일반 개체 일 수 있습니다 ( moment()허용되지 않음).

해결책

플래그 esModuleInterop를 사용하면 es6모듈 사양 에 따라 CommonJS 모듈을 가져올 수 있습니다 . 이제 가져 오기 코드는 다음과 같습니다.

// 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();

작동하며 es6 모듈 사양에서 완벽하게 유효합니다. moment스타 가져 오기의 네임 스페이스가 아니기 때문에 기본 가져 오기입니다.

하지만 어떻게 작동합니까? 보시다시피 기본 가져 오기를 수행했기 때문에 개체 default에 대한 속성을 호출 moment합니다. 그러나 우리는 순간 라이브러리 default에서 exports객체에 대한 어떤 속성도 선언하지 않았습니다 . 열쇠는 __importDefault기능 에 있습니다. CommonJS 모듈의 속성에 모듈 ( exports)을 할당 default합니다.

var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};

보시다시피 es6 모듈을 그대로 가져 오지만 CommonJS 모듈은 default키가있는 객체로 래핑됩니다 . 이렇게하면 CommonJS 모듈에서 기본값을 가져올 수 있습니다.

__importStar유사한 작업을 수행합니다-손대지 않은 esModules를 반환하지만 CommonJS 모듈을 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;
};

합성 수입품

그리고 무엇에 대한 allowSyntheticDefaultImports, 그것은 무엇인가? 이제 문서가 명확해야합니다.

Allow default imports from modules with no default export. This does not affect code emit, just typechecking.

에서 momenttypings 우리는 지정된 기본 내보내기를 가지고 있지 않으며, 단지 플래그와 함께 사용할 수 beacuse 우리가하지 말았어야 esModuleInterop합니다. 따라서 allowSyntheticDefaultImports기본 내보내기가없는 타사 모듈에서 기본값을 가져 오려는 경우 오류를보고하지 않습니다.


답변

esModuleInterop문서에 설명 된 도우미를 생성합니다. 생성 된 코드를 보면 정확히 무엇을하는지 알 수 있습니다.

//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: 모듈이 모듈이 아니면 esrequire에 의해 반환 된 것이 기본값이됩니다. 즉, commonjs모듈에서 기본 가져 오기를 사용하는 경우 전체 모듈이 실제로 기본값입니다.

__importStar이 PR 에서 가장 잘 설명됩니다 .

TypeScript는 네임 스페이스 가져 오기 (예 🙂 import * as foo from "foo"const foo = require("foo"). 여기서는 간단하지만 가져 오는 기본 개체가 기본 개체이거나 호출 / 구성 서명이있는 값이면 작동하지 않습니다. ECMAScript는 기본적으로 네임 스페이스 레코드가 일반 객체라고 말합니다.

Babel은 먼저 모듈에서 요구하고라는 속성을 확인합니다 __esModule. 이로 __esModule설정된 경우 true동작은 TypeScript의 동작과 동일하지만 그렇지 않으면 다음과 같은 네임 스페이스 레코드를 합성합니다.

  1. 모든 속성은 필수 모듈에서 뽑아내어 명명 된 가져 오기로 사용할 수 있습니다.
  2. 원래 필요한 모듈은 기본 가져 오기로 사용할 수 있습니다.

그래서 우리는 이것을 얻습니다 :

// 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이 모든 것의 동반자이며,이 값을 false로 설정해도 내 보낸 헬퍼는 변경되지 않습니다 (둘 다 여전히 동일하게 보입니다). 그러나 commonjs 모듈에 대해 기본 가져 오기를 사용하는 경우 typescript 오류가 발생합니다. 그래서이 import React from 'react'오류를 올릴 Module '".../node_modules/@types/react/index"' has no default export.경우 allowSyntheticDefaultImports입니다 false.


답변