[Typescript] 덕 타이핑, 구조적 타이핑, 명목적 타이핑

이민선(Jasmine)·2023년 12월 10일
0
post-thumbnail

우아한 Typescript with react라는 책을 읽으며 흥미로운 부분이 많았지만 타입스크립트의 구조적 타이핑이라는 개념이 특히 기억에 남았다.

이번 포스팅에서는 타입스크립트의 구조적 타이핑과 비슷하면서도 다른 자바스크립트의 덕 타이핑과 비교해보고, 구조적 타이핑과 상반된 개념인 명목적 타이핑과의 차이도 정리해보려고 한다.

덕 타이핑 (duck typing)

함수 호출 시 객체를 인자로 넘길 때 매개변수에 정의되지 않은 다른 속성까지 초과로 두고 인자로 넘기면 어떻게 될까? 덕 타이핑을 따르는 언어의 경우 아무런 오류를 내지 않는다. 대표적으로 자바스크립트가 그러하다.

const whoAmI = ({ name, age, job }) => {
  console.log(
    `내 이름은 ${name}이고 나이는 ${age}살이야 직업은 ${job}이야.`
  );
};

const person = {
  name: '김코딩',
  age: 20,
  job: '개발자',
  dreamJob: '건물주',
};

whoAmI(person);

덕 타이핑의 어원은 "만약 어떤 새가 오리처럼 걷고, 헤엄치고, 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다"("If it walks like a duck and it quacks like a duck, then it must be a duck")라는 문장이다. 객체의 타입을 판단할 때, 객체가 가진 속성과 메서드로 판단하는 것이다. 즉, 특정 객체가 특정 함수에 인자로 전달될 수 있는 타입인가를 판단할 때, 해당 함수의 매개변수에서 요구하는 속성을 모두 가지고 있다면 일단 해당 타입이라고 판단하는 것이다.

구조적 타이핑 (structural typing)

덕 타이핑과 비슷한 개념으로 구조적 타이핑이 있다. type 간 이름이 불일치하거나 객체 간 속성이 다르더라도, 특정 함수의 매개변수에서 요구하는 속성들을 모두 가지고 있고 그러한 타입이 지정되어 있는 한 일단 인자로 넘기는 것이 가능하다.

type Person = {
  name: string;
  age: number;
  job: string;
}
type PersonWithDreamJob = {
  name: string;
  age: number;
  job: string;
  dreamJob: string;
}

const whoAmI = ({name, age, job}: Person) => {
  console.log(
    `내 이름은 ${name}이고 나이는 ${age}살이야 직업은 ${job}인데 이 중에 하나라도 없으면 컴파일 단계에서 에러를 낼거야`,
  );
};

const person: PersonWithDreamJob = {
  name: '김코딩',
  age: 27,
  job: '개발자',
  dreamJob: '건물주',
}

whoAmI(person); // 오류 나지 않음

person이라는 객체는 PersonWithDreamJob이라는 type이 지정되어 있지만, Person이라는 type의 객체를 매개변수로 가지는 whoAmI 함수에 인자로 넘기는 것이 가능하다. Person type에서 요구하는 속성들을 모두 가지고 있기 때문이다.

하지만 구조적 타이핑 때문에 불편했던 점!도 있긴 했다.
아래 코드의 에러를 보자.

type CostType = {
  accomodation: number;
  food: number;
  transportation: number;
};

const getDiscountedCost = (cost: CostType) => {
  for (const key in cost) {
    // Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'CostType'.
  // No index signature with a parameter of type 'string' was found on type 'CostType'.ts(7053)
    const discountedCost = cost[key] * 0.9;
  }
};

const costA = {
  accomodation: 100,
  food: 200,
  transportation: 300,
};


getDiscountedCost(costA);

분명 매개변수 cost의 모든 key들은 number type의 value를 가지고 있다고 명시적으로 선언까지 해줬는데, 구조적 타이핑에 의해 props에 반드시 number type만 인자로 전달될 것이라는 보장이 없어서 이런 에러가 나는 것이다. 다른 excessive한 인자가 전달되더라도 number type일 것이라고 typescript에게 명확히 알려주면 되긴 하다.

type CostType = {
  accomodation: number;
  food: number;
  transportation: number;
  [key: string]: number;
};

이렇게 선언하면 더 이상 위의 오류가 나지 않는다. 구조적 타이핑이 허용하여 다른 key가 인자로 전달되더라도 value만큼은 number type일 것이라고 선언하는 것이다.

처음에는 다소 이상하다는 생각이 들었다. type 검사를 엄밀하게 하려고 자바스크립트 안쓰고 타입스크립트를 쓰는 건데, 자바스크립트의 이런 자유분방한 속성을 그대로 가져온단 말이야? 라는 생각이 들었다.
근데 다 이유가 있다고 한다. 덕 타이핑이 언뜻 보기에는 주먹구구식인 것 같지만, 다르게 해석하면 개발자에게 어느 정도 유연함을 주는 특성이기도 하다. 유연함을 주기 때문에 함수 하나만 만들어도 이런 객체, 저런 객체, 이런 상황, 저런 상황..에 다양하게 쓰일 수 있어 시간을 절약할 수 있다는 장점이 있다. 이러한 자바스크립트의 유연함을 타입스크립트에도 어느 정도 제공하자는 차원에서 구조적 타이핑을 기반으로 한다고 한다.

타입스크립트에서도 구조적 타이핑이 무조건 허용되는 것은 아니었다.

토스 기술블로그에서 재미있는 글을 읽었다.
https://toss.tech/article/typescript-type-compatibility
객체를 함수에 인자로 넘길 때, 내가 위에 제시한 예시처럼 미리 객체를 선언해놓고 함수 호출부에 객체명을 전달하면 구조적 타이핑이 허용되지만, 객체를 직접 전달하면 구조적 타이핑이 허용되지 않는다.

const whoAmI = ({name, age, job}: Person) => {
  console.log(
    `내 이름은 ${name}이고 나이는 ${age}살이야 직업은 ${job}인데 이 중에 하나라도 없으면 컴파일 단계에서 에러를 낼거야
  );
};

whoAmI({
  name: '김코딩',
  age: 27,
  job: '개발자',
  dreamJob: '건물주',
}); // 오류 발생

excessive한 속성을 함수 호출부에서 직접 전달하면, 함수 구조에 대한 오인을 유발할 수 있기 때문에 typescript 자체를 이렇게 만들었다고 한다.

덕 타이핑 vs 구조적 타이핑 비교

객체의 속성과 메서드를 바탕으로 타입 체킹을 런타임에서 동적으로 할 때 덕 타이핑이라고 한다. 자바스크립트가 이에 해당한다.
반면, 객체의 속성과 메서드를 바탕으로 타입 체킹을 정적으로 할 때 구조적 타이핑이라고 한다. 타입스크립트가 이에 해당한다.

명목적 타이핑 (nominal typing)

구조적 타이핑과 상반된 개념이다. 자바와 같은 언어에서는 명목적 타이핑을 기반으로 한다고 한다. 매개변수에 지정한 타입과 타입명이 다르게 지정된 객체를 인자로 넘길 경우 오류가 난다고 한다.

https://toss.tech/article/typescript-type-compatibility

https://vallista.kr/%EB%8D%95-%ED%83%80%EC%9D%B4%ED%95%91%EA%B3%BC-%EA%B5%AC%EC%A1%B0%EC%A0%81-%ED%83%80%EC%9D%B4%ED%95%91/

https://en.wikipedia.org/wiki/Duck_typing

profile
기록에 진심인 개발자 🌿

0개의 댓글