TIL 74 | TypeScript 타입 호환성

hyounglee·2020년 11월 1일
0

TypeScript

목록 보기
3/5
post-thumbnail

이재승님의 실전 리액트 프로그래밍을 읽고 정리한 내용입니다.

타입 호환성은 어떤 타입을 다른 타입으로 취급해도 되는지 판단하는 것이다. 할당 가능은 서브타입(subtype)으로 표현되기도 한다. 할당 가능 여부를 판단할 때는 값의 집합을 생각해보면 쉽다.

타입 A가 타입 B에 할당 가능하다 = 타입 A는 타입 B의 서브타입이다

숫자와 문자열의 타입호환성

숫자와 문자열 타입은 서로 포함 관계에 있지 않기 때문에 서로 할당이 불가능하다. 반면 숫자의 집합은 number | string 값의 집합에 포함되기 때문에 숫자는 number | string 타입에 할당이 가능하다.

function func1(a: number, b: number | string) {
  const v1: number | string = a;
  const v2: number = b; // 타입 에러
}
function func2(a: 1 | 2) {
  const v1: 1 | 3 = a; // 타입 에러
  const v2: 1 | 2 | 3 = a;
}
  1. b가 string type인 경우 v2에 할당이 불가능해진다.
  2. a가 2인 경우 v1에 할당이 불가능해진다.

인터페이스의 타입호환성

인터페이스 A가 인터페이스 B로 할당 가능하려면 다음 조건을 만족해야 한다.

  • B에 있는 모든 필수 속성의 이름이 A에도 존재해야 한다.
  • 같은 속성의 이름에 대해, A의 속성이 B의 속성에 할당 가능해야 한다.
interface Person {
  name: string;
  age: number;
}
interface Product {
  name: string;
  age: number;
}
const person: Person = {name: 'mia', age: 26};
const product: Product = person;

Person과 Product는 이름이 다르지만 모든 속성 이름과 타입이 같다. 타입 이름은 다르지만 내부 구조가 같기 때문에 서로 할당이 가능하다.

대부분의 정적 타입 언어에서는 위의 경우가 할당이 불가능하지만, 타입스크립트는 구조적 타이핑을 사용하기 때문에 가능하다. 만약 person에 age 속성이 없었다면, Person은 Product에 할당이 불가능하다.

속성이 많을수록 타입에 더 많은 제약을 가하는 것이고, 이는 해당 타입의 값의 집합이 작아지는 것을 의미한다.

선택 속성이 타입 호환성에 미치는 영향

위의 예시에서 Person의 age가 선택 속성이라면 Person은 Product에 할당이 불가능하다.

interface Person {
  name: string;
  age?: number;
}
interface Product {
  name: string;
  age: number;
}
const person: Person = {name: 'mia'};
const product: Product = person; // 타입 에러

age가 선택 속성이라면 Person의 값의 집합은 Product 값의 집합보다 커진다. 반대로 Product의 age가 선택 속성이면 Product 값의 집합이 Person 값의 집합보다 더 커지기 때문에 Person이 Product에 할당 가능해진다. 아래와 같은 경우다.

interface Person {
  name: string;
  age: number;
}
interface Product {
  name: string;
  age?: number;
}

추가 속성과 유니온 타입이 타입 호환성에 미치는 영향

추가 속성이 있으면 값의 집합은 더 작아진다. 반대로 유니온 집합이 있으면 값의 집합은 더 커진다.

interface Person {
  name: string;
  age: number;
  gender: string;
}
interface Product {
  name: string;
  age: number | string;
}

추가 속성이 있으면 값의 집합은 더 작아지므로 Person을 Product에 할당하는데 문제가 되지 않는다. 속성 타입의 범위가 넓어지면 값의 집합은 더 커지기 때문에 Person이 Product에 할당 가능하다는 것에 변함이 없다.

함수의 타입 호환성

함수는 호출하는 시점에 문제가 없어야 할당 가능하다. 함수 타입 A가 함수 타입 B로 할당 가능하기 위해서는 아래의 조건을 충족해야 한다.

  • A의 매개변수 개수가 B의 매개변수 개수보다 적어야 한다.
  • 같은 위치의 매개변수에 대해 B의 매개변수가 A의 매개변수로 할당 가능해야 한다.
  • A의 반환값은 B의 반환값으로 할당 가능해야 한다.
type F1 = (a: number, b: string) => number;
type F2 = (a: number) => number;
type F3 = (a: number) => number | string;
let f1: F1 = (a, b) => 1;
let f2: F2 = a => 1;
let f3: F3 = a => 1;
f1 = f2;
f2 = f1; // 타입 에러
f2 = f3; // 타입 에러

F2보다 F1의 매개변수가 더 많으므로 F1은 F2로 할당 불가능하다.
F3의 반환 타입은 F1의 반환타입으로 할당이 불가능하므로 F3는 F2로 할당 가능하지 않다.

배열의 map 메서드를 통해 살펴보는 함수의 타입 호환성

function addOne(value: number) {
  return value + 1;
}
const result = [1, 2, 3].map<number>(addOne);
// ( value: number, index: number, array: number[]) => number;

addOne 함수는 map 메서드의 매개변수로 할당이 가능하다. 참고로 map 메서드의 제네릭으로 입력한 number는 매개변수 함수의 반환 타입을 의미한다. 주석은 map 메서드가 입력받는 함수의 타입을 의미하고, addOne 함수는 이 타입에 할당이 가능하다.

addOne 함수의 매개변수가 4개 이상이었거나, 타입이 달랐거나, 반환 타입이 달랐다면 문제가 될 수 있다. 이렇게 함수의 타입 호환성은 호출하는 쪽에서 생각하면 이해하는데 도움이 된다.

profile
(~˘▾˘)~♫•*¨*•.¸¸♪ ❝ 쉽게만 살아가면 재미없어 빙고 .ᐟ ❞

0개의 댓글