ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [TS] d.ts 파일 작성시 이모저모
    programing/Language 2020. 5. 17. 18:56

    안녕하세요, Einere입니다.

    (광고 차단 기능을 꺼주시면 감사하겠습니다.)


    발단

    최근 CRACreate React App와 TypeScript를 이용해 토이 프로젝트를 진행하고 있었습니다.

    TS를 사용하다 보니, 어쩔 수 없이 군데군데 사용자 정의 타입들이 생기게 되었습니다.

    각 컴포넌트에 분산되어 있는 타입들을 한 파일로 모아서 관리하고 싶은 욕구가 뿜뿜 솟아났습니다.

     

    .d.ts vs .ts

    하나의 파일로 관리를 할 때, 해당 파일의 확장자를 .d.ts로 할 것인지 .ts로 할 것인지 고민을 했습니다.

    공식 레퍼런스를 보니, 어떤 선언(declaration)이든 export 키워드를 통해 익스포트 될 수 있다고 하며, 예제에서도 대부분 .ts파일을 사용합니다.

    또한 구현이 없는 선언을 ambient라고 말하며, 이 앰비언트들은 .d.ts파일에 서술합니다. (C언어의 .h파일과 유사합니다.)

    저는 결국 .d.ts파일을 만들기로 결정했는데, 이유는 다음과 같습니다.

    • module 키워드를 통해, 절대 경로로 임포트 하여 사용할 수 있다.
    • 다른 라이브러리처럼, .d.ts파일을 작성해보고 싶었다.
    • 타입이 많지 않고 간단하여, 하나의 .d.ts파일에 다 때려 박아도 복잡하지가 않을 것이다.

    JS를 하면서 상대 경로로 특정 모듈을 가져와 사용하는 방법은 많이 경험해봤으므로, 절대 경로로 사용해보고 싶은 이유가 제일 컸습니다. ㅎㅎ

     

    types.d.ts를 작성하자

    공식 문서에 따르면 module키워드와 따옴표로 묶인 이름을 이용해서 추후 임포트 될 수 있는 모듈을 선언할 수 있다고 합니다.

    // /src/@types/types.d.ts
    declare module 'myModule' {
        export namespace myNamespace {
            type Foo = string;
            interface Bar {
                baz: number;
            }
        }
    }
    

    위 코드에서는 myModule라는 모듈을 선언하고, 그 안에 myNamespace라는 네임스페이스를 만들고, 그 안에 Foo라는 타입과 Bar라는 인터페이스를 선언했습니다.

    이제, 다른 파일 내부에서 다음과 같이 사용할 수 있습니다.

    import * as React from 'react';
    import {myNamespace} from 'myModule';
    
    const foo:myNamespace.Foo = 'foo';
    console.log(foo);
    

     

    Enum은 안되네?

    그런데 저는 Enum도 .d.ts파일에 넣고 싶었습니다. 그런데 자꾸 Module not found: Can't resolve 'myModule' in '...'에러만 발생했습니다. 분명 Enum도 타입의 한 종류이며, type alias는 앰비언트이며 Enum도 앰비언트라서 .d.ts에 들어가도 되지 않나? 싶었습니다. 하지만 컴파일러는 계속 module not found 에러를 내뱉습니다..

    그럼 안된다 치고, 그 이유를 나름대로 찾아봤습니다. 그러나, 딱히 명쾌한 해답은 못 찾았습니다. 그나마 찾은 것이라곤

    • Enum은 타이핑이 아니므로 .ts에 정의하는 게 좋다
    • 굳이 .d.ts에 넣고 싶으면 union type 혹은 const enum을 써라
    • tsconfig.json의 include 범위 안에 선언해라

    정도네요.

    이미 types.d.ts./src폴더 아래에 있으며, const enum도 에러가 납니다. 😭

     

    yarn start가 아닌 yarn build를 해보니, myModule이라는 모듈을 찾을 수 없으니 yarn add myModule로 추가하라는 안내문구가 뜨네요. 아마 tsc가 node_modules에서 해당 모듈을 못 찾아서 띄우는 듯합니다. 

     

    또한 ts.config에 isolatedModulestrue인 경우 const enum에 접근할 수 없는데, CRA로 만든 프로젝트는 이 옵션을 지울 수 없습니다.

     

    좀 더 올바른 방법

    .d.ts를 쓰는 경우

    DaeSeon Jeong님의 ppt를 보니, 올바르게 커스텀 타입들 .d.ts파일로 분리하는 방법은 다음과 같습니다.

     

    ts.config의 typeRoots를 지정하기

    이전에는 index.d.ts를 ./src/@types 내부에 정의했지만, 더 올바른 위치는 ./@types/someModuleName 내부입니다.

    따라서 저는 custom-types라는 폴더를 만들고, 그 안에 index.d.ts를 만들었습니다. (모듈 이름을 동일하게 해야 합니다!)

    // ./@types/custom-types/index.d.ts
    declare module 'custom-types' {
        export namespace customTypes {
            type foo = string;
        }
    }
    

     

    그리고 ts.config에 typesRoots속성을 정의합니다.

    {
      "compilerOptions": {
        "target": "es5",
        "lib": [
          "dom",
          "dom.iterable",
          "esnext"
        ],
        ...
        "typeRoots": ["./node_modules/@types", "./@types"]
      },
      "include": [
        "src"
      ]
    }
    

    현재 프로젝트에서 npm을 통해 설치한 모듈이 있으므로 ./node_modules/@types를 먼저 추가하고, 자신이 작성한 커스텀 타입들은 ./@types 내부에 폴더별로 정의할 것이므로 위와 같이 설정해줍니다.

     

    하지만 이래도 enum과 관련된 에러는 사라지지 않습니다. 결국 .ts파일로 분리하기로 했습니다..

     

    결론

    일단 명확한 원인은 찾지 못했습니다. 아마 enum이 runtime에 특정한 형태로 변환되기 때문에 complie time에 에러가 나는 것으로 유추하고 있습니다.

    그냥 .ts파일과 상대 경로를 이용해 사용하는 게 속이 편하긴 합니다.. 😅

    댓글

Designed by black7375.