esModuleInterop 설정
요약
esm 의 default 는 export const default 와 비슷하다. 다만 변수가 아닌 키워드다.
TS 의 가짜 import
타입스크립트는 기본적으로 esm 스럽게 모듈을 다룬 것처럼 보이게 한다.
esm 에서 모듈을 표준 방식으로 가져오는 키워드인 import 가 있지만, 타입스크립트에서는 컴파일 되고 나면 cjs 모듈 시스템 (require 함수 사용) 으로 바뀔 수 있다.
관련 영상도 참고하면 좋다.
CJS 모듈 가져오는 방식
요약하자면, 하나의 모듈을 하나의 객체로 묶어서 처리한다.
exports.foo = () => {}
exports.bar = () => {}
exports.baz = () => {}
module.exports = function main() {}이런 코드가 있다면, cjs 는 main 함수의 property 에 named-export 를 추가한다.
main.foo = () => {}
main.bar = () => {}
main.baz = () => {}ESM 모듈 가져오는 방식
반면 esm 은 default 는 지정된 named export 이다.
{
default : function main() {}
// named-exports
foo : () => {},
bar : () => {},
baz : () => {}
}직접 겪은 사례
바벨 패키지 중 generator 와 traverse 가 있다. 자체적으로 타입을 제공하지 않기 때문에 @babel/__generator 와 @babel/__traverse 를 추가적으로 설치해야한다.
npm i @babel/generator @babel/traverse
npm i --save-dev @babel/__generator @babel/__traverse하지만 문제는 여기 있다. 타입 default 선언 위치가 다른 것이었다.
기본적으로 ts 의 esModuleInterop 옵션은 false 이기 때문에 아래 임포트는 export default 를 가져온다.
import traverse from '@babel/traverse' // export default 를 가져옴근데 해당 모듈이 cjs 로 되어 있으면 default가 없기 때문에 아래와 같이 가져오라고 한다.
import * as traverse from '@babel/traverse'이러면 export default 를 가져오지 않고 하나의 객체로 묶어서 cjs 처럼 가져올 수 있게 된다.
혹은 esModuleInterop 옵션을 켜준다.
import traverse from '@babel/traverse'
// traverse === { default: ... , named-exports ... }그래서 어쩔 수 없이 아래와 같은 이상한 코드로 해결했다.
import _traverse from '@babel/traverse'
const a = _traverse.default as any
const traverse = a as typeof _traverse중간 단계에서 any 를 통해 타입 초기화 를 했다. 매우 좋지 않은 방식이지만, 다른 방법은 떠오르지 않았다.