TS for OOP Programmers를 읽고 나서

7999 단어 typescripttypescript

TypeScript for OOP Programmers

타입스크립트를 좀 더 자세히 알고 친해지고 싶어서 읽게 되었다.

C#이 제일 익숙해서 for OOP Programmers를 골랐다.

다음은 이를 읽고 나서 정리한 것이다.

집합으로서의 타입

처음 타입스크립트를 접했을 때는 타입스크립트의 타입도 C#의 자료형이나 클래스와 같을 것이라 생각했다.

타입스크립트를 검색해봤을 때 C#을 만든 사람들이 개발에 참여했다고 되어있어서 더 그렇게 생각한 것 같다.

하지만 타입스크립트에서는 타입 A이거나 타입 B일 수 있는 유니언 타입이 있었고 여러 타입을 결합할 수 있는 교차 타입도 있어서 혼란이 왔었다.

핸드북에서는 타입스크립트에서의 타입을 집합으로 생각하길 권장한다.

타입이 집합이기에 하나의 값이 여러 집합에 동시에 속할 수 있다는 것이다.

예를 들어서 string 집합과 number 집합에 속할 수 있는 값은 string | number 집합에 속하는 값으로 설명할 수 있다.

C#에서도 타입을 비교적 자유롭게 유동적으로 사용할 수 있는 object, var, dynamic이 있지만 여러 타입을 동시에 가질 수는 없었기에 이 점이 굉장히 인상 깊었다.

Structural. not Nominal

타입스크립트의 객체는 단일 타입을 가지지 않는다고 한다.

타입스크립트의 타입 시스템은 명목적(Nominal)이 아닌 구조적(Structural) 타입 시스템이라 한다.

interface Pointlike {
  x: number;
  y: number;
}
interface Named {
  name: string;
}

function printPoint(point: Pointlike) {
  console.log("x = " + point.x + ", y = " + point.y);
}

function printName(x: Named) {
  console.log("Hello, " + x.name);
}

const obj = {
  x: 0,
  y: 0,
  name: "Origin",
};

printPoint(obj);
printName(obj);

먼저 위 코드를 명목적 타입 시스템의 시선으로 보면 obj 객체는 printPoint, printName 함수의 인자가 될 수 없다.

왜냐하면 obj 객체의 타입이 Pointlike나 Named가 아니기 때문이다.

코드를 구조적 타입 시스템의 시선으로 보면 obj 객체는 printPoint, printName 함수의 인자가 될 수 있다.

왜냐하면 obj 객체가 Pointlike의 x, y 속성을 가지고 있고 Named의 name 속성을 가지고 있기 때문이다.

머리를 한 대 맞은 듯한 느낌이었다.

정말 영리한 방법이라고 생각되었다.

이는 마치 명목적 타입 시스템의 암시적 형 변환과 같은 효과를 낸다고 생각한다.

class Empty {}

function fn(arg: Empty) {
  // do something?
}

// No error, but this isn't an 'Empty' ?
fn({ k: 10 });

이 코드도 재밌었다.

매개 변수의 타입은 Empty이고 인자의 타입은 { k: number; }이다.

인자 타입이 Empty 타입을 포함하고 있으므로 잘 작동한다.

이들을 앞서 비유한 집합으로 설명해보자면 매개 변수 타입이 인자 타입의 부분집합이면 되는 것이다.

구조적 타입 시스템을 설명하는 놀라운 예시는 하나 더 있다.

class Car {
  drive() {
    // hit the gas
  }
}
class Golfer {
  drive() {
    // hit the ball far
  }
}
// No error?
let w: Car = new Golfer();

두 클래스는 이름이 다르지만 구조는 같다.

그래서 마지막 줄과 같은 식으로 초기화할 수 있다.

좋은 코드 같지는 않고 쓸 일도 없을 것 같지만 구조적 타입 시스템의 이해를 돕기에는 좋은 예시라고 생각한다.

typeof

class Car {}

typeof(new Car()) // result: "object"

구조적 타입 시스템에서는 런타임에서 typeof를 이용해서 타입을 얻는 것이 불가능하다고 한다.

마치며

예상외로 많은 걸 배웠다.

읽어보길 잘했다.

핸드북의 남은 챕터들을 보면서 얼마나 더 새로운 것들을 보게 될지 궁금하다.

좋은 웹페이지 즐겨찾기