[TS] 개발자처럼 사용하기 2 : Type-Only Imports and Exports

Zero·2023년 10월 22일
1
post-thumbnail

살펴보게된 계기

타입스크립트로 프로젝트를 작업을 하면서 import에 type을 적을 수 있는 것을 알고 type을 붙이면서 작업을 하기 시작하였습니다. 나중에는 왜 붙이는지조차 모르고 붙이고 있었는데 사용하는 이유를 알고나서 적으면 좋을 것 같아 찾아보게 되었습니다.

Type-Only Imports and Exports?

import type을 검색했는데 위와 같이 검색결과가 나왔습니다. 오로지 타입만 Import 가져오고 Exports 내보낸다는건데 무슨말일까?


// ./post.ts
interface IPostDetail {
    // ...
}

export function makePost(postInfo: IPostDetail) {
    // ...
}

// ./PostPage.ts
import { makePost, IPostDetail } from "./post";

function makePosts(options: IPostDetail) {
    makePost(options);
    makePost(options);
}

TypeScript는 import elision 기능을 제공하여 JavaScript로 컴파일 될 때 타입으로 사용되는 것들을 모두 지워버립니다. JavaScript에서는 type이 없기 때문이죠.

위에 코드가 컴파일시에는 다음과 같아집니다.


export function makePost(postInfo) {
    // ...
}

// ./PostPage.ts
import { makePost } from "./post";

function makePosts(options) {
    makePost(options);
    makePost(options);
}

위 예제의 경우 자동으로 IPostDetail 해당 인터페이스를 지워버립니다.

가장 먼저, export 되는 것이 변수인지 타입인지 모호한 경우가 몇가지 있습니다. 아래 예시의 경우 MyThing이 변수인지 타입인지 알 수 없습니다.

import { isMyPost, makePost, PostDetail } from "./post"

export {isMyPost, makePost }

다음과 같이 export를 해을 때 무엇이 타입인지 알 수 없습니다. 타입스크립트에게 이것은 타입이니까 지워져야해라고 말해야하지만 타입스크립트는 알 방법이 없습니다.

또 사용이 되어야하는 타입이라도 elision 기능때문에 지워지는 불상사가 일어나게 되었습니다.

그 결과로 타입스크립트 3.8 버전에서는 type-only imports and exports 기능이 추가가 되었습니다.

import type { PostDetail } from "./post";

export type { PostDetail };

import type은 타입 표기와 선언에 사용될 선언만 import 합니다. 이는 항상 완전히 제거되므로, 런타임에 남아있는 것은 없습니다.
마찬가지로, export type은 타입 문맥에 사용할 export만 제공하며, 이 또한 TypeScript의 출력물에서 제거됩니다.

클래스는 런타임에 값을 가지고 있고 디자인-타임에 타입이 있으며 사용은 상황에-따라 다르다는 것을 유의해야 합니다. 클래스를 import 하기 위해 import type을 사용하면, 확장 같은 것은 할 수 없습니다.

import type { Component } from "react";
interface ButtonProps {
    // ...
}
class Button extends Component<ButtonProps> {
    //               ~~~~~~~~~
    // error! 'Component' only refers to a type, but is being used as a value here.
    // ...
}

또 런타임에 사용되지 않는 imports 를 제어하기 위한 새로운 컴파일러 importsNotUsedAsValues flag도 추가되었습니다. 이 플래그는 3개의 설정 값 중 하나를 사용할 수 있습니다.

  • remove : 기존 컴파일러의 동작과 동일하게 런타임에 사용되지 않는 imports 들을 제거합니다.
  • preserve : 타입 정보는 제거 되지만, 해당 모듈의 imports 는 유지합니다. 따라서 위의 imports/side-effects 문제상황을 해결할 수 있습니다.
  • error : 이는 preserve 옵션과 동일하게 동작하지만 타입으로써만 import를 사용했을 때에 오류를 발생시킵니다.

만약 변수가 아닌 것들이 우연하게 import 되는 것을 보호하고 싶을 때 유용할 것 입니다.
해당 기능에 대한 더 많은 정보를 위해서 pull request 와 연관된 변경사항 등을 살펴볼 수 있습니다.

주의사항

다음과 같이 불확실한 import type은 할 수 없습니다.

// 'Foo'만 타입인가? 혹은 모든 import 선언이 타입인가?
// 이는 명확하지 않기 때문에 오류로 처리합니다.
import type Foo, { Bar, Baz } from "some-module";
//     ~~~~~~~~~~~~~~~~~~~~~~
// error! A type-only import can specify a default import or named bindings, but not both.

결론

typescript에게 해당 import와 export는 타입이라는 것을 친절하게 알려줌으로서 에러를 방지할 수 있습니다. 또한 사용자도 해당 타입명이 타입인지 변수명인지 보고 알 수 있습니다.

type을 import한다는 것은 해당 줄을 번들시 제거해줌으로서 에러를 방지해줍니다. 앵귤러와 같이 만약 타입이 필요하다면 넘겨줍니다.

참고자료

profile
0에서 시작해, 나만의 길을 만들어가는 개발자.

0개의 댓글