TypeScript - Learning TypeScript Chap. 14 Syntax Extensions

이소라·2023년 7월 15일
0

TypeScript

목록 보기
27/28

Chapter 14. Syntax Extensions

  • TypeScript와 같은 상위 집합 언어에 특정 새로운 런타임 기능으로 JavaScript 구문을 확장하는 방식은 다음과 같은 이유로 나쁜 사례로 간주합니다.

    • 런타임 구문 확장이 최신 버전 JavaScript의 새로운 구문과 충돌할 수 있다는 점이 가장 중요합니다.
    • 언어를 처음 접하는 프로그래머가 JavaScript가 끝나는 곳과 다른 언어가 시작하는 곳을 이해하기 어렵게 만듭니다.
    • 상위 집합 언어 코드를 사용하고 JavaScript를 내보내는 트랜트파일러의 복잡성을 증가시킵니다.
  • 따라서 초기 TypeScript 설계자들이 TypeScript 언어로 JavaScript의 세 가지 구문 확장을 도입했다는 것이 유감스럽습니다.

    • 클래스

      • 사양이 승인됨에 따라 JavaScript 클래스에 맞춘 클래스
      • useDefineForClassFields 동작과 매개변수 속성을 제외하고 TypeScript 클래스는 JavaScript의 클래스와 동일하게 동작하고 작동합니다.
    • 열거형 (enum)

      • 키와 값의 일반 객체와 유사한 간단한 구문
      • enum은 유용한 경우가 종종 있어 일부 프로젝트에서 여전히 사용됩니다.
    • 네임스페이스 (namespace)

      • 코드를 구조화하고 배열하는 최신 모듈보다 앞선 해결책
      • 사실상 새로운 프로젝트는 더 이상 namespace를 사용하지 않습니다.
  • TypeScript는 decorator에 대한 실험적인 제안(experimental proposal)을 채택했습니다.



14.1 Class Parameter Properties

Tip

  • 클래스를 많이 사용하는 프로젝트나 클래스 이점을 갖는 프레임워크가 아니라면 클래스 매개변수 속성을 사용하지 않는 것이 좋습니다.
  • TypeScript는 클래스의 매개변수 속성 (parameter proprety)을 선언하기 위한 단축 구문을 제공합니다.
    • 속성은 클래스 생성자의 시작 부분에 동일한 타입의 멤버 속성으로 할당됩니다.
    • 생성자의 매개변수 앞에 readonly 또는 public, protected, private 제한자 중 하나를 배치하면 TypeScript가 동일한 이름과 타입의 속성을 선언하도록 지시합니다.
class Engineer {
  constructor(readonly area: string) {
    console.log(`I work in the ${area} area.`);
  }
}

// 타입 : string
new Engineer("mechanical").area;
  • 매개변수 속성은 클래스 생성자의 맨 처음에 할당됩니다.
    • 또는 기본 클래스로부터 파생된 클래스인 경우, 매개변수 속성은 super()를 호출한 이후 할당됩니다.
    • 매개변수 속성은 다른 매개 변수 또는 클래스 속성과 혼합될 수 있습니다.
class NamedEngineer {
  fullName: string;
  
  constructor(
  name: string, 
  public area: string
  ) {
    this.fullName = `${name}, ${area} engineer`;
  }
}
  • 대부분의 프로젝트는 앞에서 언급했던 단점으로 인해 매개변수 속성을 완전히 사용하지 않는 것을 선호합니다.

    • 또한 매개변수 속성은 새로운 # 클래스 private 필드 구문과 함께 사용할 수 없습니다.
  • 반면에 클래스 생성을 매우 선호하는 프로젝트에서는 매개변수 속성을 사용하면 정말 좋습니다.

    • 이 경우 매개변수 속성은 매개변수 속성 이름과 타입을 두 번 선언해야하는 문제를 해결해줍니다.



14.2 Experimental Decorators

Tip

  • ECMAScript 버전이 decorator 구분으로 승인될 때까지 가능하면 decorator를 사용하지 않는 것이 좋습니다.
  • TypeScript decorator 사용을 권장하는 Angular나 Nest.JS와 같은 프레임워크 버전에서 작업하는 경우 프레임워크 설명서에서 decorator를 사용하는 방법을 알려줍니다.
  • 클래스를 포함하는 많은 다른 언어에서는 클래스와 클래스의 멤버를 수정하기 위한 일정의 런타임 로직으로 주석을 달거나 decorating할 수 있습니다.
  • decorator 함수는 @와 함수 이름을 먼저 배치해 클래스와 멤버에 주석을 달 수 있도록 하는 JavaScript를 위한 제안입니다.
@myDecorator
class MyClass { /* ... */ }
  • ECMAScript에서는 아직 decorator를 승인하지 않았으므로 TypeScript 버전 4.7.2에서 기본적으로 decorator를 지원하지 않습니다.
    • ECMAScript에서 decorator가 stage 3이 되었고, TypeScript 버전 5.0부터 decorator를 지원합니다.
  • TypeScript는 decorator의 오래된 실험적인 버전을 코드에서 사용할 수 있도록 제공하는 experimentalDecorators 컴파일러 옵션을 제공합니다.
    • experimentalDecotators 컴파일러 옵션은 tsc CLI 또는 TSConfig 파일을 통해 활성화할 수 있습니다.
// tsconfig.json
{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}
  • decorator의 각 사용법은 decorating하는 엔티티가 생성되자마자 한 번 실행됩니다.
  • 각 종류의 decorator(접근자, 클래스, 메서드, 매개변수, 속성)는 decorating하는 엔티티를 설명하는 서로 다른 인수 집합을 받습니다.
function logOnCall(target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value;
  console.log('[logOnCall] I am decorating', target.constructor.name);
  
  descriptor.value = function (...args: unknown[]) {
    console.log(`[descriptor.value] calling '${key}' with:`, ...args);
  }
}

class Greeter {
  @logOnCall
  greet(message: string) {
    console.log(`[greet] Hello, ${messgage}`);
  }
}

new Greeter().greet("you");
// Output log:
// "[logOnCall] I am decorating", "Greeter"
// "[descriptor.value] Calling 'greet' with:", "you"
// "[greet] Hello, you"



14.3 Enums

Tip

  • 자주 반복되는 리터럴 집합이 있고, 그 리러털 집합을 공통 이름으로 설명할 수 있으며, enum으로 전환했을 때 훨씬 더 읽기 쉬운 경우에만 enum을 사용합니다.
  • enum은 각 값에 대해 친숙한 이름을 사용한 객체에 저장된 리터럴 값 집합으로 생각할 수 있습니다.
  • JavaScript는 enum 구문을 포함하지 않으므로 enum을 배치해야 하는 곳에 일반적인 객체를 사용합니다.
    • 예를 들어 HTTP 상태 코드를 숫자로 저장하고 사용할 수 있지만, 개발자들은 친숙한 이름으로 키를 지정해 객체에 저장하는 방식을 사용하여 더 읽기 쉽도록 했습니다.
const StatusCodes = {
  InternalServerError: 500,
  NotFound: 404,
  Ok: 200,
  // ...
} as const;

StatusCodes.InternalServerError; // 500
  • TypeScript에서 enum과 같은 객체를 사용할 때 까다로운 점은 해당 객체의 값 중 하나여야 함을 나타내는 훌륭한 타입 시스템 방법이 없다는 것입니다.
    • 한 가지 일반적인 방법은 keyoftypeof 타입 제한자를 함께 사용하여 하나의 값으로 제한하는 것이지만, 이렇게 하려면 상당한 양의 구문을 입력해야 합니다.
// 타입 : 200 | 404 | 500
type StatusCodeValue = (typeof StatusCodes)[keyof typeof StatusCodes];

let statusCodeValue : StatusCodeValue;

// Ok
statusCodeValue = 200;
// Error: Type '-1' is not assignable to 'StatusCodeValue'.
statusCodeValue = -1;
  • TypeScript는 타입이 number 또는 string인 리터럴 값들을 갖는 객체를 생성하기 위한 enum 구문을 제공합니다.
    • enum은 enum 키워드로 시작해 객체 이름(일반적으로 PascalCase), 그 다음 쉼표(,)로 구분된 키를 포함한 {} 객체입니다.
    • 각 키는 초깃값 앞에 선택적으로 =를 사용할 수 있습니다.
enum StatusCode {
  InternalServerError = 500,
  NotFound = 404,
  Ok = 200,
}

StatusCode.InternalServerError; // 500
  • 클래스 이름처럼 enum 이름은 type annotation에서 타입 이름으로 사용할 수 있습니다.
let statusCode: StatusCode;

statusCode = StatusCode.Ok; // Ok
statusCode = 200; // Ok
  • 컴파일된 JavaScript에서 enum은 이에 상응하는 객체로 컴파일됩니다.
    • enum의 각 멤버는 해당 값을 갖는 객체 멤버의 키가 되고 그 반대의 경우도 마찬가지입니다.
var StatusCode;
(function (StatusCode) {
    StatusCode[StatusCode["InternalServerError"] = 500] = "InternalServerError";
    StatusCode[StatusCode["NotFound"] = 404] = "NotFound";
    StatusCode[StatusCode["Ok"] = 200] = "Ok";
})(StatusCode || (StatusCode = {}));
StatusCode.InternalServerError;
  • enum은 'JavaScript에 새로운 런타임 구문 구조를 절대 추가하지 않는다'는 TypeScript의 일반적인 만트라를 위반합니다.
    • enum은 개발자가 학습해야 하는 JavaScript가 아닌 새로운 구문을 제시하며, preserveConstEnums와 같은 옵션에 대한 몇 가지 결함을 갖습니다.
  • 다른 한편으로 enum은 알려진 값 집합을 명시적으로 선언하는데 매우 유용합니다.
    • enum은 TypeScript와 VSCode 소스 저장소에서 모두 광범위하게 사용됩니다.

14.3.1 Automatic Numeric Values

  • enum 멤버는 명시적인 초깃값을 가질 필요가 없습니다.
    • 값이 생략되면 TypeScript는 첫 번째 값을 0으로 시작하고 각 후속 값을 1씩 증가시킵니다.
    • enum 멤버의 값이 고유하고 키 이름과 연결되는 것외에는 중요하지 않다면 TypeScript에서 enum 멤버의 값을 선택하도록 하는 것은 좋은 옵션입니다.
enum VisualTheme {
  Dark, // 0
  Light, // 1
  System, // 2
}
  • 내보낸 JavaScript는 값이 명시적으로 설정된 것처럼 보입니다.
var VisualTheme;
(function (VisualTheme) {
    VisualTheme[VisualTheme["Dark"] = 0] = "Dark";
    VisualTheme[VisualTheme["Light"] = 1] = "Light";
    VisualTheme[VisualTheme["System"] = 2] = "System";
})(VisualTheme || (VisualTheme = {}));
  • 숫잣값이 있는 enum에서 명시적 값을 갖지 않는 모든 멤버는 이전 값보다 1 더 큰 값을 갖습니다.
enum Direction {
  Top = 1,
  Right,
  Bottom,
  Left,
}
  • 출력된 JavaScript에서는 나머지 멤버가 명시적 값 2, 3, 4를 가진 것처럼 보입니다.
var Direction;
(function (Direction) {
    Direction[Direction["Top"] = 1] = "Top";
    Direction[Direction["Right"] = 2] = "Right";
    Direction[Direction["Bottom"] = 3] = "Bottom";
    Direction[Direction["Left"] = 4] = "Left";
})(Direction || (Direction = {}));

Tip

  • enum의 순서를 수정하면 기본 번호가 변경됩니다.
  • 만약에 데이터베이스 같은 곳에 enum 값을 저장하고 있다면 enum 순서를 변경하거나 항목을 제거할 때 주의해야 합니다.
    • 저장된 번호가 더 이상 코드가 예상한 것과 같지 않기 때문에 데이터가 갑자기 손상될 수 있습니다.

14.3.2 String-Valued Enums

  • enum은 멤버로 숫자 대신 문자열값을 사용할 수 있습니다.
enum LoadStyle {
  AsNeed = "as-needed",
  Eager = "eager",
}
  • 문자열로 멤버를 갖는 enum의 출력 JavaScript는 숫잣값을 멤버로 갖는 enum과 구조적으로 동일합니다.
var LoadStyle;
(function (LoadStyle) {
    LoadStyle["AsNeed"] = "as-needed";
    LoadStyle["Eager"] = "eager";
})(LoadStyle || (LoadStyle = {}));
  • 문자열값을 갖는 enum은 읽기 쉬운 이름으로 공유 상수의 별칭을 지정하는 데 유용합니다.

    • 문자열 리터럴 union 타입을 사용하는 대신 문자열값을 갖는 enum을 사용하면 더 강력한 편집기 자동 완성과 해당 속성의 이름 변경이 가능해집니다.
  • 문자열값의 한 가지 단점은 TypeScript에 따라 자동으로 계산할 수 없다는 것입니다.

    • 숫잣값이 있는 멤버 뒤에 오는 enum 멤버만 자동으로 계산할 수 있습니다.
enum Wat {
  FristString = "first",
  SomeNumber = 9000,
  ImplicitNumber, // Ok (value 9001)
  AnotherString = "another",
  NotAllowed, // Error: Enum member must have initializer.
}

Tip

  • 이론적으로 숫자와 문자열 모두 멤버로 갖는 enum을 만들 수 있습니다.
  • 하지만 실제로 이런 형태의 enum은 불필요하고 혼란스러울 수 있으므로 사용해서는 안됩니다.

14.3.3 Const Enums

  • enum은 런타임 객체를 생성하므로 리터럴 값 union을 사용하는 일반적인 전략보다 더 많은 코드를 생성합니다.
  • TypeScript는 const 제한자로 enum을 선언해 컴파일된 JavaScript 코드에서 객체 정의와 속성 조회를 생략하도록 지시합니다.
const enum DisplayHint {
  Opaque = 0,
  Semitransparent,
  Transparent,
}

let displayHint = DisplayHint.Transparent;
  • 컴파일된 JavaScript 코드에는 enum 선언이 모두 누락되고 enum 값에 대한 주석을 사용합니다.
let displayHint = 2 /* DisplayHint.Transparent */;
  • enum 객체 정의를 생성하는 것이 여전히 바람직한 프로젝트라면 enum 정의 자체가 존재하도록 만드는 preserveConstEnums 컴파일러 옵션을 사용합니다.
    • 여전히 값은 enum 객체에 접근하는 대신에 리터럴 값을 직접 사용합니다.
var DisplayHint;
(function (DisplayHint) {
    DisplayHint[DisplayHint["Opaque"] = 0] = "Opaque";
    DisplayHint[DisplayHint["Semitransparent"] = 1] = "Semitransparent";
    DisplayHint[DisplayHint["Transparent"] = 2] = "Transparent";
})(DisplayHint || (DisplayHint = {}));
let displayHint = 2 /* DisplayHint.Transparent */;
  • preserveConstEnums는 생성된 JavaScript 코드의 크기를 줄이는 데 유용하지만 TypeScript 코드를 변환하는 모든 방법이 이를 지원하는 것은 아닙니다.



14.4 Namespaces

warning

  • 기존 패키지에 대한 DefinitelyTyped 타입 정의를 작성하지 않는 한 namespace를 사용하지 마세요.
    • namespace는 최신 JavaScript Module 의미 체계와 일치하지 않습니다.
    • 자동 멤버 할당은 코드를 읽는 것을 혼란스럽게 만들 수 있습니다.
    • .d.ts 파일에서 namespace를 접할 수 있습니다.
  • ECMAScript Module이 승인되기 전에 웹 애플리케이션이 출력 코드 대부분을 브라우저에 따라 로드되는 하나의 파일로 묶는 것이 일반적이었습니다.

    • 이러한 거대한 하나의 파일은 종종 프로젝트의 서로 다른 영역에 걸쳐서 중요한 값에 대한 참조를 위한 전역 변수를 생성했습니다.
    • 하나의 파일 출력을 위해 만들어진 프로젝트에는 코드 영역과 전역 변수를 구성하는 방법이 필요했습니다.
  • TypeScript 언어는 namespace라 부르는 내부 모듈(internal module) 개념을 가진 하나의 해결책을 제공했습니다.

    • namespace는 객체의 멤버로 호출할 수 있는 내보내진(exported) 콘텐츠가 있는, 전역으로 사용 가능한 객체입니다.
    • namespace는 namespace 키워드와 {} 코드 블록으로 정의합니다.
    • namespace 블록의 모든 코드는 함수 클로저 내에서 평가됩니다.
namespace Randomized {
  const value = Math.random();
  console.log(`My value is ${value}`);
}
  • 출력된 JavaScript는 Randomized 객체를 생성하고 함수 내부의 블록 내용을 확인하기 때문에 value 변수는 namespace 외부에서는 사용할 수 없습니다.
var Randomized;
(function (Randomized) {
    const value = Math.random();
    console.log(`My value is ${value}`);
})(Randomized || (Randomized = {}));

14.4.1 Namespace Exports

  • namespace를 유용하게 만드는 핵심 기능은 콘텐츠를 namespace 객체의 멤버로 만들어 내보내는 기능입니다.
    • 이 작업 덕분에 코드의 다른 영역에서 namespace 이름으로 해당 멤버를 참조할 수 있습니다.
namespace Settings {
  export const name = "My Application";
  export const version = "1.2.3";
  export function describe() {
    return `${Settings.name} at version ${Settings.version}`;
  }
  console.log("Initializing", describe());
}
console.log("Ininitialized", Settings.describe());
  • 출력된 JavaScript는 이런 값을 사용할 때 내부와 외부 모두 Settings의 멤버로 항상 참조됨을 보여줍니다.
var Settings;
(function (Settings) {
    Settings.name = "My Application";
    Settings.version = "1.2.3";
    function describe() {
        return `${Settings.name} at version ${Settings.version}`;
    }
    Settings.describe = describe;
    console.log("Initializing", describe());
})(Settings || (Settings = {}));
console.log("Ininitialized", Settings.describe());
  • 출력 객체에 var를 사용하고 내보낸 콘텐츠를 해당 객체의 멤버로 참조하도록 하면, namespace가 여러 파일에 걸쳐 분할되어 작성되었더라도 아주 잘 작동합니다.
// settings/constants.ts
namespace Settings {
  export const name = "My Application";
  export const version = "1.2.3";
}
// settings/describe.ts
namespace Settings {
  export function describe() {
    return `${Settings.name} at version ${Settings.version}`;
  }
  console.log("Initializing", describe());
}
// index.ts
console.log("Ininitialized", Settings.describe());
  • 함께 연결된 출력 JavaScript는 대략 다음과 같습니다.
// settings/constants.ts
var Settings;
(function (Settings) {
    Settings.name = "My Application";
    Settings.version = "1.2.3";
})(Settings || (Settings = {}));
// settings/describe.ts
(function (Settings) {
    function describe() {
        return `${Settings.name} at version ${Settings.version}`;
    }
    Settings.describe = describe;
    console.log("Initializing", describe());
})(Settings || (Settings = {}));
console.log("Ininitialized", Settings.describe());
  • 단일 파일과 다중 파일 형식 모두 런타임에서 출력된 객체는 3개의 키가 있는 객체입니다.
const Settings = {
  describe: function describe() {
    return `${Settings.name} at version ${Settings.version}`;
  },
  name: "My Application",
  version: "1.2.3",
}
  • namespace를 사용할 때의 주요 차이점은 서로 다른 파일로 분할될 수 있고 멤버가 namespace의 이름으로 여전히 참조할 수 있다는 점입니다.

14.4.2 Nested Namespaces

  • namespace는 다른 namespace 내에서 namespace를 내보내거나 하나 이상의 마침표(.)를 사용해서 무한으로 중첩할 수 있습니다.

  • 다음 두 개의 namespace 선언은 동일하게 작동합니다.

namespace Root.Nested {
  export const value1 = true;
}

namespace Root {
  export namespace Nested {
    export const value2 = true;
  }
}
  • 둘 다 구조적으로 동일한 코드로 컴파일됩니다.
(function (Root) {
    var Nested;
    (function (Nested) {
        Nested.value1 = true;
    })(Nested = Root.Nested || (Root.Nested = {}));
})(Root || (Root = {}));
  • 중첩된 namespace는 namespace로 구성된 더 큰 프로젝트의 구역들 사이에 더 자세한 설명을 적용할 수 있는 편리한 방법입니다.
    • 많은 개발자가 그들의 프로젝트 이름을 최상위 namespace로 사용하고, 프로젝트의 각 주요 영역을 자식 namespace로 사용합니다.

14.4.3 Namespaces in Type Definitions

  • namespace는 DefinitelyTyped 타입 정의에 유용합니다.

    • 많은 JavaScript 라이브러리, 특히 jQuery와 같은 오래된 웹 애플리케이션에서 고정적으로 사용하는 라이브러리는 전통적인 <script> 태그를 사용해 웹 브라우저에 포함되도록 설정합니다.
    • namespace를 타이핑할 때는 모든 코드에 사용 가능한 전역 변수, 즉, namespace로 완벽하게 감싼 구조를 생성한다는 것을 나타내야 합니다.
  • 또한 많은 브라우저 지원 JavaScript 라이브러리는 더 현대적인 모듈 시스템에 삽입되고 전역 namespace를 생성하기 위해 설정됩니다.

    • TypeScript는 module 타입 정의에 export as namespace와 그 뒤에 전역 이름을 포함하고, 해당 이름을 사용해서 module을 전역으로 사용할 수 있음을 나타냅니다.
  • 예를 들어 module에 대한 다음 선언 파일은 value를 내보내고 전역으로 사용할 수 있습니다.
// node_modules/@types/my-example-lib/index.d.ts
export const value: number;
export as namespace libExample;
  • 타입 시스템은 import("my-example-lib")와 window.libExample 모두 number 타입의 value 속성과 함께 module을 반환한다는 것을 알고 있습니다.
// src/index.ts
import * as libExample from "my-example-lib"; // Ok
const value = window.libExample.value; // Ok

14.4.4 Prefer Modules Over Namespaces

  • namespace를 사용하는 대신 ECMAScript module를 사용하여 최신 표준에 맞게 이전 예제의 settings/constants.ts 파일과 settings/describe.ts 파일을 재작성할 수 있습니다.
// settings/constants.ts
export const name = "My Application";
export const version = "1.2.3";
// settings/describe.ts
import { name, version } from "./constants";

export function describe() {
  return `${name} at version ${version}`;
}

console.log("Initializing", describe());
// index.ts
import { describe } from "./settings/describe";

console.log("Initialized", describe());
  • namespace로 구조화된 TypeScript 코드는 Webpack과 같은 최신 빌더에서 사용하지 않는 파일을 제거하는 것이 쉽지 않습니다.
    • namespace는 ECMAScript Module처럼 파일 간에 명시적으로 선언되는 것이 아니라 암시적으로 연결을 생성하기 때문입니다.
    • 따라서 TypeScript namespace가 아닌 ECMAScript Module을 사용해 런타임 코드를 작성하는 것이 훨씬 더 좋습니다.



14.5 Type-Only Imports and Exports

  • 타입 전용 가져오기(import)와 내보내기(export)는 매우 유용하며 내보내진 JavaScript 출력에 어떠한 복잡성도 추가하지 않습니다.

  • TypeScript의 트랜스파일러는 JavaScript 런타임에 사용되지 않으므로 파일의 가져오기와 내보내기에서 타입 시스템에서만 사용되는 값을 제거합니다.

  • 예를 들어 다음 index.ts 파일은 action 변수와 ActivistArea 타입을 생성한 다음, 나중에 독립형 내보내기 선언을 사용해서 두 개를 모두 보냅니다.

// index.ts
const action = { area: "people", name: "Bella Abzug", role: "politician" };

type ActivistArea = "nature" | "people";

export { action, ActivistArea };
  • index.js로 컴파일할 때 TypeScript의 트랜스파일러는 독립형 내보내기 선언에서 ActivistArea를 제거해야 함을 알고 있습니다.
// index.js
const action = { area: "people", name: "Bella Abzug", role: "politician" };

export { action };
  • 다시 내보낸 타입을 제거하려면 TypeScript 타입 시스템에 대한 지식이 필요합니다.

  • 한 번에 하나의 파일을 작동하는 Babel 같은 트랜스파일러는 각 이름이 타입 시스템에서만 사용되는지 여부를 알 수 있는 TypeScript 타입 시스템에 접근할 수 없습니다.

    • TypeScript의 isolatedModules 컴파일러 옵션은 코드가 TypeScript가 아닌 다른 도구에서 변환되는지 확인할 때 매우 유용합니다.
  • TypeScript는 exportimport 선언에서 개별적으로 가져온 이름 또는 전체 { ... } 객체 앞에 type 제한자를 추가할 수 있습니다.

    • 이렇게 하면 타입 시스템에서만 사용된다는 것을 나타냅니다.
    • 패키지의 기본 가져오기(default import)를 type으로 표시할 수도 있습니다.
// index.ts
import { type TypeOne, value} from "my-example-types";
import type { TypeTwo } from "my-example-types";
import type DefaultType from "my-example-types";

export { type TypeOne, value };
export type { DefaultType, TypeTwo };
  • index.ts가 출력 index.js로 변환될 때 value 가져오기와 내보내기만 유지됩니다.
// index.js
import { value } from "my-example-types";

export { value };
  • 일부 TypeScript 개발자는 타입으로만 사용되는 가져오기를 더 명확하게 하기 위해 타입 전용 가져오기를 선호합니다.
    • 가져오기가 타입 전용으로 표시된 경우, 이를 런타임 값으로 사용하려고 하면 TypeScript 오류가 발생합니다.
import { ClassOne, type ClassTwo } from "my-example-types";

// Ok
new ClassOne();
// Error: 'ClassTwo' cannot be used as a value because it was imported using 'import type'.
new ClassTwo(); 
  • 내보내진 JavaScript에 복잡성을 더하는 대신, 타입 전용 가져오기와 내보내기는 코드 일부를 제거할 수 있을 때 TypeScript 외부의 트랜스파일러에 명확하게 알립니다.



참고

0개의 댓글