본문 바로가기
Typescript

Typescript | 타입 호환성, 대수타입

by 리잼 2024. 10. 29.
반응형

타입 호환성

타입 호환성이란 예를 들어 A와 B 두개의 타입이 존재할 때 A 타입의 값을 B 타입으로 취급해도 괜찮은지 판단하는 것을 의미

만약 A 타입의 값이 B 타입의 값으로 취급 되어도 괜찮다면 호환된다고 하고 안된다면 호환되지 않는다고 한다.

  • 예를 들어 다음 그림처럼 Number 타입과 Number Literal 타입이 있을 때 
    서브 타입인 Number Literal 타입의 값을 슈퍼 타입인 Number 타입의 값으로 취급하는 것은 가능
  • 반대로는 불가능

Number 타입이 더 큰 타입이기 때문에 Number 리터럴 타입은 같은타입으로 취급할 수 없다.

다음과 같은 코드는 문제가 되지 않는다.

let num1: number = 10;
let num2: 10 = 10;

num1 = num2;
  • Number 타입의 변수 num1을 선언하고 값으로 10을 할당
  • 10(Number Literal) 타입의 변수 num2를 선언하고 값으로 역시 10을 할당
  • 이때 num1에 num2의 값을 저장하는건 가능
  • 변수 num1의 타입이 더 큰 타입(Number 타입)이기 때문

반대의 경우 에러가 발생한다

TS에서는 슈퍼타입 값을 서브타입의 값으로 취급하는 것을 허용하지 않는다.

  • 서브 타입의 값을 슈퍼 타입의 값으로 취급하는 것은 업 캐스팅 이라고 부르고 반대는 다운 캐스팅이라고 부른다
  • 업캐스팅은 모든 상황에 가능하지만 다운 캐스팅은 대부분의 상황에 불가능하다고 할 수 있다.

객체 타입의 호환성

객체 타입간의 호환성도 변수와 동일한 기준으로 판단한다.

모든 객체 타입은 각각 다른 객체 타입들과 슈퍼-서브 타입의 관계를 갖는다.

따라서 업 캐스팅은 허용하고 다운 캐스팅은 허용하지 않는다.

  • 예시 코드
type Animal = {
  name: string;
  color: string;
};

type Dog = {
  name: string;
  color: string;
  breed: string;
};

let animal: Animal = {
  name: "기린",
  color: "yellow",
};

let dog: Dog = {
  name: "돌돌이",
  color: "brown",
  breed: "진도",
};

animal = dog; // ✅ OK
dog = animal; // ❌ NO
  • Animal의 타입이 Dog 타입의 슈퍼 타입이기 때문에 animal = dog 는 가능하다.
  • 역시 반대로는 불가능하다.

초과 프로퍼티 검사

type Book = {
  name: string;
  price: number;
};

type ProgrammingBook = {
  name: string;
  price: number;
  skill: string;
};

let book: Book;
let programmingBook: ProgrammingBook = {
  name: "한 입 크기로 잘라먹는 리액트",
  price: 33000,
  skill: "reactjs",
};

book = programmingBook; // ✅ OK
programmingBook = book; // ❌ NO

let book2: Book = { // 오류 발생
  name: "한 입 크기로 잘라먹는 리액트",
  price: 33000,
  skill: "reactjs",
};

skill 프로퍼티가 Book 타입에 없기 때문에 에러가 발생함

  • 이런 초과 프로퍼티 검사는 단순히 변수를 초기화 할 때 객체 리터럴을 사용하지만 않으면 발생하지 않는다.

따라서 다음과 같이 값을 별도의 다른 변수에 보관한 다음 변수 값을 초기화 값으로 사용하면 발생하지 않는다.

(...)

let book3: Book = programmingBook; // 앞서 만들어둔 변수

 

초과 프로퍼티 검사는 함수의 매개변수에도 동일하게 발생한다.

function func(book: Book) {}

func({ // 오류 발생
  name: "한 입 크기로 잘라먹는 리액트",
  price: 33000,
  skill: "reactjs",
});

에러 발생

  • 함수의 매개변수에 인수로 값을 전달하는 과정도 변수를 초기화 하는 과정과 동일하다.
  • 따라서 초과 프로퍼티 검사가 발동하게 된다.
  • 타입 검사를 피하고 싶다면 다음과 같이 변수에 미리 값을 담아둔 다음 변수값을 인수로 전달하면 된다.


대수타입

여러개의 타입을 합성해서 만드는 타입을 일컫는다

합집합, 교집합 타입이 존재하고 합집합은 Union, 교집합은 Intersection 타입 이라고 부른다.

 

Union Type

다음과 같이 string, number 의 Union 타입을 정의할 수 있다.

// 합집합 타입 - Union 타입
let a: string | number;

//따라서 이제 변수 a에는 number 타입과 string 타입에 해당하는 값이라면 뭐든 저장할 수 있다.
a = 1;
a = "hello";
  • 유니온 타입에 참여하는 타입의 갯수는 제한이 없다.

Union Type 으로 배열 타입 정의하기

let arr: (number | string | boolean)[] = [1, "hello", true];

Union Type 과 Object Type

다음과 같이 여러개의 객체 타입의 유니온 타입도 정의 가능하다.

type Dog = {
  name: string;
  color: string;
};

type Person = {
  name: string;
  language: string;
};

type Union1 = Dog | Person;

이렇게 정의된 유니온 타입은 다음과 같이 교집합이 존재하는 두 집합으로 표현할 수 있다.

(...)

let union1: Union1 = { // ✅
  name: "",
  color: "",
};

let union2: Union1 = { // ✅
  name: "",
  language: "",
};

let union3: Union1 = { // ✅
  name: "",
  color: "",
  language: "",
};

 

다음과 같은 객체는 포함하지 않는다.

let union4: Union1 = { // ❌
  name: "",
};

그림으로 표현한 Union1 타입

Intersection Type

다음과 같이 string 과 number 의 인터섹션 타입을 정의할 수 있다.

// never 타입으로 추론된다
let variable: number & string;
  • number 타입과 string 타입은 서로 교집합을 공유하지 않는 서로소 집합이므로
    변수 variable의 타입은 결국 never 타입으로 추론된다.
  • 대다수의 기본 타입들 간에는 서로 공유하는 교집합이 없기 때문에 이런 인터섹션 타입은 보통 객체 타입들에 자주 사용된다.

Intersection 타입과 객체 타입

type Dog = {
  name: string;
  color: string;
};

type Person = {
  name: string;
  language: string;
};

type Intersection = Dog & Person;

let intersection1: Intersection = {
  name: "",
  color: "",
  language: "",
};

위 코드를 그림으로 표현하면 이러하다.

 

반응형