게시글

5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭
강좌2 - TypeScript - 6년 전 등록 / 4년 전 수정

블로그 타입스크립트 전환 후기 - import, export

이전 글에서 이어집니다. 지난 시간에 타입스크립트 환경 세팅을 했죠.

@types 패키지들을 깔고 나면, 기존 코드에서 import 부분부터 에러가 납니다.

import React, { PureComponent } from 'react';

위에서 React가 default export가 아니라고 에러가 나죠. 수정하려면 다음과 같이 해야 합니다.

import * as React from 'react';
import { PureComponent } from 'react';

뭔가 두 줄이 돼서 마음에 들지 않았습니다. tsconfig.json 옵션에서 esModuleInterop이라는 옵션을 켜면 위처럼 한 줄로 쓸 수는 있습니다. 문법적으로 * as React를 쓰는 것이 올바른 방법이 맞다고 생각하고는 있지만 편의를 위해서 esModuleInterop을 켜서 사용합니다. es 모듈 강좌 

잘 생각해보면 아래와 같은 경우는 React가 export default 되어 있을 때나 가능한 방식입니다.

import React, { PureComponent } from 'react';

하지만 React는 export default React가 아니라 그냥 export = React 되어 있습니다. 따라서 default가 없기 때문에 개별 export들을 모아주는 * as React가 맞다고 판단하긴 했습니다.

axios같은 경우는 타입 정의에 export default Axios로 되어 있기 때문에 아래처럼 하면 됩니다.

import axios from 'axios'; // default 가져오기

가끔가다 @types가 없거나 정의가 틀린(이 경우가 더 화가 납니다, connect-flash같은...) 패키지가 생깁니다. 타입스크립트도 많은 시행착오를 거치면서 언어가 만들어졌기 때문에 과거의 코드가 남아있어 사람들 골치를 썩입니다. 예를 들어 ES6 모듈 대신에 네임스페이스를 써서 글로벌 환경을 오염시킨다거나, 모듈 선언을 export = e 같은 것으로 했거나요.

어찌됐든 정의가 없는 모듈은 정의(.d.ts 파일)를 만들어주어야 합니다. 처음에는 정의가 없어도 에러가 안 나서 귀찮게 왜 만드냐고 하실 수 있는데 나중에 noImplicAny 옵션을 켜면 에러가 뿜뿜합니다.

tsconfig.json

{
  "typeRoots": [
     "./types",
     "./node_modules/@types"
  ]
}

지난 시간의 tsconfig.json엣 typeRoots가 타입을 저장할 폴더를 의미합니다. 제가 만든 커스텀 타입들은 types에 넣어줍니다.

types/

index.d.ts
can-use-dom.d.ts
react-filepicker.d.ts
filestack-react.d.ts
express-http-proxy.d.ts
connect-flash.d.ts

types 폴더 내부 d.ts 파일들입니다. 위 패키지들은 타입이 없거나 틀려서 제가 임의로 만들었습니다. 몇 개 파일을 살펴보죠

index.d.ts

import { } from 'express';

declare global {
  interface Window {
    swUpdate: boolean;
    Notification: any;
    adsbygoogle: any[];
    __INITIAL_STATE__: string;
    Kakao: any;
    FB: any;
    google: any;

    devToolsExtension(): () => void;
  }

  interface Error {
    code?: number;
  }
}

제가 임의로 만든 정의를 모아둔 파일입니다. declare global로 기존에 정의되어있던 인터페이스를 확장했습니다. 뭔가 전역 객체를 확장해서 꺼림칙하긴 하지만 딱히 다른 방법이 떠오르지 않네요. 개별적으로 extend하긴 너무 지저분하고요. import {} from 'express'는 파일을 모듈로 만들기 위한 꼼수입니다. 타입스크립트의 경우 import나 export 선언이 위에 없는 경우 일반 스크립트로 칩니다.

connect-flash.d.ts

declare module 'connect-flash' {
  global {
    namespace Express {
      interface Request {
        flash(): { [key: string]: string[] };

        flash(message: string): any;

        flash(event: string, message: string): void;
      }
    }
  }

  import express = require('express');

  function flash(): express.RequestHandler;

  export default flash;
}

이건 connect-flash를 제가 다시 타입 정의한 파일입니다. declare module '패키지명'으로 하시면 됩니다. 꼭 패키지를 100% 정확하게 정의할 필요는 없습니다. 그냥 자신이 쓰는 API 정도만 정의하시면 됩니다. 위의 경우처럼 다른 패키지의 객체를 확장해야 하는 경우가 있어 조금 헤맸습니다.

이렇게 정의한 타입들이 너무 훌륭하다고 생각하면 @types에 배포하시면 됩니다. 저는 passport-kakao와 passport-naver를 수정해서 배포했고, react-fb-like와 react-vote에 타입을 추가해서 배포했습니다. 타입스크립트를 하면서 오픈소스 컨트리뷰터가 되는 1석2조!

참고로 이미 ts로 만들어진 라이브러리인 경우, 굳이 .d.ts를 따로 만들 필요가 없습니다. 만들었다간 괜히 .d.ts랑 .ts 파일이 충돌납니다.

이제 나머지 파일들은 개별적으로 interface나 type을 정의하면 됩니다. 저는 리액트 환경에서 진행해서 리액트 타입을 분석하느라 골머리를 좀 썩혔습니다. 다음 포스팅에서 이어집니다.

조회수:
0
목록
투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.
5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭

댓글

3개의 댓글이 있습니다.
2년 전
타입이 없으면 화난다는데 저는 왜 따구로 글쓴거에 화가나죠..
2년 전
네 꺼지세요 ^^
5년 전
안녕하세요 좋은 내용들 잘 보고 있습니다. CommonJS 를 완충하는 용도입니다 뭐가 올바르다고 하는 부분은 아닌거 같아요 주로 회사 내규로 default 바로 로드는 없다고 생각하고 사용하자고 lint 정의는 하는 경우는 있습니다. `import {default, foo, bar} = 'abc'` 정정 하셔야 될 것 같습니다. `import React from 'react` `import * as React from 'react'; React.default...;` 는 같습니다.
5년 전
React.default라는 건 없습니다. React는 export default를 하지 않습니다. * as는 완전 다른 겁니다.
5년 전
안녕하세요! 좋은 내용의 글 감사합니다.

`esModuleInterop` 문제가 생기셨다는 내용이 있어서 어떤 문제가 발생했는지 혹시 공유해주실 수 있을까요?

그리고 CommonJS/AMD 모듈의 import 대해 정리한 좋은 글이 있어 공유드립니다.
https://blog.bigfont.ca/import-m-require-module-vs-import-as-m-from-module/
5년 전
import React from 'react'랑 import * as React from 'react'는 자바스크립트 문법 상 매우 다르고 실제 기능도 다르지만, 타입스크립트에서 esModuleInterop을 쓰면 대체해버립니다. 이 기능을 많이들 켜시는데, 실제 자바스크립트 모듈 시스템과는 어긋납니다.