Frontend Engineer - 박준형

Published on

타입스크립트의 구조적 타이핑

Authors
  • Name
    Twitter

타입스크립의 타입시스템은 구조적으로 타입이 맞다면 타입체킹을 통과시킨다.

공식문서의 Type Compatibility에 나와있다.

덕 타이핑이란 무엇인가

덕 타이핑은 객체의 변수 메서드 집합이 객체의 타입을 결정하는것을 말한다.

위의 사진처럼 머리가 초록색이고 날개가 있고 발이 조류의 발이라면 청둥오리인가? 사람이 청둥오리의 탈을 쓰고있는것은 중요하지않다. 덕타이핑에서는 생김새가 청둥오리 같으면 청둥오리라고 부를 수 있는것이다.

덕 타이핑의 장단점

단점

  • 정적 타이핑 언어처럼 런타임 전 컴파일이나 타입체킹을 하는 시간이 없으니, 함수의 의도에 맞지 않는 코딩이 가능하다는 것이다. 어떤게 들어가든 없는게 들어가도 에러가 발생하지 않는다
  • 타입이 맞지 않거나, 체크가 안되어도 어떻게든 실행은 된다. 런타임 중 해당 함수를 실행하긴 하지만 그럼에도 에러만 출력될뿐이지 실행은 된다.

장점

  • 당장 빠른 개발이 가능하다.
  • 유연하게 작성할 수 있어서 생산성을 높힐 수 있다

하나의 함수로 여러 객체단위의 행동을 유연하게 작성할 수 있다는 점은 강타입언에서는 동일하게 사용하기 위해 제너릭 혹은 상속(다형성)을 위해서 어떠한 테크닉을 배워야한다. 하지만 Javascript나 Python은 그딴거 필요없이 구현해서 사용하면 된다.

구조적 타이핑이란 무엇인가

구조적 타이핑은 구조적 타입 시스템(Structual Type System) 이라고도 불린다. 실제 구조와 정의에 의해 결정되는 타입 시스템의 한 종류다.

명목적타입 시스템(Nominal Type Sytem) 언어인 Java, C#등과 다르고, 런타임에 타입을 체크하는 덕 타이핑과는 또 다르다

Typescript의 타입 호환성은 구조적 타이핑을 기반으로 한다. 구조적 타이핑은 멤버만에 기반하여 유형을 연관시키는 방법이다

class FakePerson {
  name: string
}

interface Person {
  name: string
}

let person: Person

person = new FakePerson() // 타입 통과! 이유는 구조적 타이핑에 의해서다.

C# 혹은 Java 같이 강타입 언어에서는 FakePerson 클래스가 Person의 구현체라고 명시적으로 나타내지 않기 때문에 동등한 코드가 오류가 날 것이다.

Typescript의 구조적타이핑 시스템은 Javascript가 일반적으로 작성되는 방식을 기반으로 설계되었다. Javascript는 함수 표현식이나 객체 리터럴 같은 익명 객체를 널리 사용하기 때문에 명목적 타이핑 보다는 구조적 타이핑 시스템을 사용하여 Javascript 라이브러리에서 발견되는 관계의 종류를 표현하는것이 훨씬 자연스럽다고한다.

Typescript의 타입시스템은 컴파일 시점에 알 수 없는 특정 연산을 안전하게 처리할수 있도록 해준다. 타입 시스템에 이러한 속성이 있을떄 건전하지 않다라고 한다. 타입스크립트가 건전하지 않은 동작을 허용하는 곳은 신중하게 고려되었다.

타입스크립트의 구조적 타이핑 기본 규칙

Typesciprt의 구조적 타이핑의 기본 규칙은 x와 y가 최소한 동일한 멤버를 가지고 있는 x는 y와 호환된다는것이다.

interface Person {
  name: string
}
let person: Person

// fakePerson's inferred type is { name: string; age: number; }
let fakePerson = { name: '박준형', age: 28 }
person = fakePerson

person에 fakePerson를 할당할 수 있는지 확인하기 위해 타입스크립트의 컴파일러는 Person의 각 프로퍼티를 검사하여 해당되는 호환 프로퍼티를 찾는다. 이경우엔 name이라는 멤버가 필요한데 fakePerson에는 name이 존재하므로 할당이 허용되는것이다.

interface Person {
  name: string
}
let fakePerson = { name: '박준형', age: 28 }

function sayHello(person: Person) {
  console.log(`Hello ${person.name}`)
}

sayHello(fakePerson)

fakePerson에는 age라는 추가 멤버 속성이 있지만, 오류가 발생하지는 않는다. 타입 호환성을 확인할때에는 대상 유형의 멤버만 고려된다. 비러한 비교 프로세스는 각 멤버와 하위 멤버의 유형을 탐색하면서 재귀적으로 진행된다.

위의 예제에서 명목적 타입 시스템을 기준으로 Person 을 사용하도록 의도한 코드일것이다. 하지만 구조적 타이핑 언어에서는 전혀 다른 개념으로 이해해야한다. Person 의 속성에 해당하는 값이 값을 넣는 타입에 속성으로 존재하는가? 로 이해해야한다. 이 개념은 집합과 관계가 있는데, 구조적 타이핑은 명목적 타이핑과 같은 명확한 상속 관계(A-B)를 지향하기보다 집합으로 포함한다는 개념을 지향하기 때문이다!

interface Person {
  name: string
}
let fakePerson: Person = { name: '박준형', age: 28 } // Error
  • 같은 속성의 타입이 있는지를 체크하기 때문에 위 두 인터페이스는 호환된다.
    • 중복되는 범위가 있다면 재사용할 수 있고, 생산성 있는 코드를 쉽게 생산할 수 있다.
  • 단점으로 의도하지 않았지만 동일한 타입을 가지는 경우 의도치 않게 동일한 타입으로 간주 될 수 있다.

자세한것은 공식문서 Type Compatibility를 확인해보도록 해보자

덕 타이핑과 구조적 타이핑의 차이

  1. 덕 타이핑은 런타임에 타입을 체크할 수 도있고 안할 수 도있다.
  2. 구조적 타이핑은 타입 시스템 기반에서 컴파일 타임(혹은 타입체커)에서 타입을 체크한다.
  3. 두 방법 모두 객체의 변수, 메서드 같은 필드를 기반으로 타입을 체크할 수도 안할 수도있지만, 덕 타이핑은 동적 타이핑에서, 구조적 타이핑은 정적 타이핑에서 쓰인다.
  4. 덕 타이핑은 다형성 관점에서 주목해야하고, 구조적 타이핑은 타입 체킹 관점이다.