[TypeScript] interface vs type

은승균·2023년 2월 3일

Typescript

타입스크립트는 자바스크립트의 superset이라고 불린다. MS에서 만들었고, 현재 4.9버전까지 나왔다. (2023년 02월 3일 기준)

자바스크립트는 정적 타입을 체크하지 않는다. 자바스크립트 자체적으로 number, string, object 등의 타입을 가지긴 하지만, 코드베이스가 일관적으로 작성되고 있는지는 체크해주지 않는다.

타입스크립트는 자바스크립트의 상위 레이어에서 타입 시스템을 가지고 있는 자체적인 레이어이다.

두 관계가 상당히 흥미로웠는데, 타입스크립트가 자바스크립트로 컴파일 되는 과정을 자바스크립트로 진행한다는 것이었다. 자바스크립트에는 타입이 없는데? 타입체크와 컴파일을 진행한다고 한다.

Type

타입스크립트는 자바스크립트 코드에서 타입을 추론할 수 있는 능력이 있다. 개발자가 명시적으로 타입을 지정해줄 수도 있지만, 그러지 않더라도 어떤 타입인지 유추할 수 있다.

특정 변수에 값을 할당하면, 해당 변수는 해당 값의 타입으로 결정된다.

  const name = 'my name'; // string

타입 지정

타입을 개발자가 직접 지정해줄 수도 있다.

  const myName: string = 'name';

기본적으로 제공하는 원시 타입이 아닌 개발자가 타입을 선언하여 사용할 수도 있다.

  interface User {
    name: string;
    age: number;
  }

 const user: User = {
   name: '머스크',
   age: 26,
 };

user 변수는 User 인터페이스를 따른다는 것을 알 수 있다.

타입을 선언하는 방법에는 interface가 아닌 type alias를 이용할 수도 있다.

 type User = {
   name: string;
   age: number;
 };

type alias는 긴 타입을 간단하게 사용할 수 있도록 해주는 용도로 사용할 수도 있다.

type Currency = 'won' | 'dollar' | 'yen' | 'pound';
type Response = AxiosResponse<DataType> | null;

여러 타입을 | 을 이용하여 묶어서 나타낼 수 있다.

지원 범위

type alias는 모든 타입에 이름을 달아줄 수 있다.
interface는 객체 타입에만 이름을 달아줄 수 있다.

type Genre = '공포'; ( o )
interface Genre '공포; ( x ) 

type? interface?

타입스크립트를 사용해보면서 두 가지 방법으로 타입을 선언해서 지정해줄 수 있었는데, 두 가지 모두 잘 작동하는 것을 볼 수 있었다.
그렇다면 어떤 상황에서 type을 사용하고 어떤 상황에서 interface를 사용해야할까?

결론적으로 interface를 기본으로 사용하고, 꼭 필요한 경우에만 type을 사용하라는 것이 공식문서의 조언이다.

선언 병합 Declaration Merging

두 개의 가장 큰 차이점은 선언 병합 지원의 유무이다.
선언 병합이랑 같은 이름으로 선언된 타입의 프로퍼티를 컴파일 시점에 하나로 묶어주는 것이다.

interface는 선언 병합을 지원한다. type은 지원하지 않는다.

// interface
interface Window {
	title: string; 
}

interface Window {
 	ts: import("typescript"); 
}

const window = getWindow();
const src = `const a = "Hello World!"`;
window.ts.transpileModule(src, {}); // 메서드 사용 가능


// type alias
type Window = {
  title: string;
};

type Window = {
  ts: import("typescript");
}
// Error: Duplicate Identifier 'Window'.

타입스크립트 팀은 개방-폐쇄 원칙에 따라 열려있는 JavaScript 객체의 동작 방식과 비슷하게 연결하도록 interface를 설계했다고 한다.

Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an interface are available in type, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable.
대부분의 경우 두 가지 모두 자유롭게 사용할 수 있다. interface의 거의 모든 기능은 type에도 있다. 가장 큰 자이는 type은 새로운 프로퍼티 추가에 열려있지 않는다는 것이고, interface는 항상 열려있다는 점이다.

그렇다면 확장 가능한 방식과 확장이 불가능한 방식을 나누어 제공하는 이유는 무엇일까?

한 테코블 글의 글쓴이의 생각을 빌려 말하자면, 타입의 속성이 추가될 수 있는 것을 고려할 것이냐 아니냐를 개발할 때 생각하면서 개발해야하기 때문이다.

let,var는 재할당이 가능하지만, const는 재할당이 불가능하다. 이런 느낌이지 않을까

이런 것이 명확하게 드러나는 사례는 외부 라이브러리를 사용할 때 원하는 속성을 추가하여 사용하는 경우이다.

// @emotion/react/types
export interface Theme{}

// emotion.d.ts
import '@emotion/react';

declare module '@emotion/react' {
	export interface Theme {
    	colors: typeof Colors;
    }
}

이전에 Flutter webview와 연결할 때 window 객체에 메서드가 추가되어 사용하기 위해 Window 타입에 프로퍼티를 추가한 경험이 있다.

이런 방식으로 라이브러리를 사용하는 개발자가 선언 병합을 활용해 원하는 속성을 선언해 사용할 수 있다는 장점이 있다.
반면에 위에서 본 것 처럼 type alias는 선언 병합이 불가능하다.

결론

그래서 type alias보단 interface를 사용하고, union type 혹은 tuple type을 반드시 써야되는 상황에서는 type alias를 사용하도록 권장하고 있다.

확장 가능한 타입을 위해서는 interface를 사용하고, 확장이 불가능한 타입, 혹은 확장을 할 필요없는 타입을 위해서는 type alias를 사용하면 될 것 같다.

0개의 댓글