타입스크립트 이해하기

DevSeong2·2023년 9월 12일
0

TypeScript

목록 보기
4/4
post-thumbnail

"한 입 크기로 잘라먹는 타입스크립트 - 인프런" 강의를 요약하고 덧붙인 글입니다.

타입스크립트를 이해하기 위해

  • 어떤 기준으로 타입을 정의하는지
  • 어떤 기준으로 타입간의 관계를 정의하는지
  • 어떤 기준으로 타입의 오류를 검사하는지
    타입스크립트의 구체적인 원리와 동작 방식에 대해 살펴보겠습니다.

타입은 집합이다

타입스크립트의 타입은 '집합'입니다. 집합은 동일한 속성을 갖는 여러개의 요소들을 하나의 그룹으로 묶은 단위를 말합니다. 예를 들어, number 타입은 정수, 실수, 양수, 음수, 지수 등 모든 숫자 값들을 묶어 놓은 집합입니다.
Number Literal 타입은 하나의 값만 포함하는 아주 작은 집합이라고 볼 수 있습니다.
모든 Number Literal 타입은 Number 타입이라는 집합에 포함되는 부분 집합으로 볼 수 있습니다. 타입스크립트의 모든 타입들은 집합으로써 서로 포함하고 또 포함되는 관계를 갖습니다.

다른 타입을 포함하는 타입을 슈퍼 타입(부모 타입), 반대는 서브 타입(자식 타입)이라고 합니다.

타입 호환성

타입 호환성은 어떤 타입을 다른 타입으로 취급해도 괜찮은지 판단하는 것을 말합니다.

예를 들어 Number 타입과 Number Literal 타입이 있을 때 서브 타입인 Number Literal 타입의 값을 슈퍼 타입인 Number 타입의 값으로 취급하는 것은 가능합니다. 그러나 반대로는 불가능합니다.

Number 타입이 Number Literal 타입보다 더 큰 타입이기 때문입니다.

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

num1 = num2; // ⭕
num2 = num1; // ❌

집합의 관점에서 본다면 Number Literal 타입의 값 10은 모든 숫자를 포함하는 Number 타입에 포함되지만 그 역은 성립하지 않습니다.

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

타입 계층도와 함께 기본타입 살펴보기

타입스크립트 타입 계층도는 기본 타입들간의 집합으로써 부모-자식 관계를 설명합니다.

unknown 타입 (전체 집합)

unknown 타입은 타입 계층도의 최상단에 위치합니다. unknown 타입은 모든 타입의 슈퍼타입, 모든 타입은 unknown 타입의 부분집합입니다.

따라서 unknown 타입 변수에는 모든 타입의 값을 할당할 수 있습니다(업 캐스팅).

let a: unknown = 1;
let b: unknown = 'a';
let c: unknown = true;
let d: unknown = null;
let e: unknown = undefined;
let f: unknown = [];
let g: unknown = {};
let h: unknown = () => {};

반대로는 불가능합니다. unknown 타입의 값은 any를 제외한 어떤 타입의 변수에도 할당할 수 없습니다(다운 캐스팅).

let unknownValue: unknown;

let a: number = unknownValue; // ❌

never 타입 (공집합 타입)


never 타입은 타입 계층도에서 가장 아래에 위치합니다. never 타입은 모든 집합의 부분집합 즉, 공집합입니다. 공집합은 원소의 개수가 0인 집합을 말합니다.

따라서 never 타입에 해당하는 값은 아무것도 없습니다. 어떤 값도 반환할 수 없는 무한루프나 비정상적 종료 등의 상황에 주로 사용됩니다.

공집합은 모든 집합의 부분집합. 그러므로 never 타입은 모든 타입의 서브 타입으로 모든 타입으로 업캐스팅 할 수 있습니다.

반대는 불가능합니다. 그래서 어떤 값도 저장되어서는 안 되는 변수의 타입으로 활용하기에 좋습니다.

let neverVar: never;

let a: number = neverVar; 
let b: string = neverVar; 
let c: boolean = neverVar;    
let d: null = neverVar;  
let e: undefined = neverVar;  
let f: [] = neverVar;     
let g: {} = neverVar;

let never1: never = 10; // ❌

void 타입


void 타입은 undefined 타입의 슈퍼타입입니다. 따라서 반환값을 void로 선언한 함수에서 undefined을 반환 해도 오류가 발생하지 않습니다. undefined 타입은 void 타입의 서브 타입이므로 업캐스팅이 가능하기 때문입니다.

function noReturnFuncA(): void {
  return undefined;
}

function noReturnFuncB(): void {
  return;
}

function noReturnFuncC(): void {}

void 타입의 서브타입은 undefined 타입과 never 타입 밖에 없습니다. 따라서 void 타입에는 undefined, never 이외에 다른 타입의 값을 할당할 수 없습니다.

any 타입 (치트키)

any 타입은 사실상 타입 계층도를 완전히 무시합니다. 모든 타입의 슈퍼타입이 될 수도 있고 모든 타입의 서브 타입이 될 수도 있습니다.

any 타입은 모든 타입으로 다운캐스트 할 수 있으며 또 모든 타입은 any 타입으로 업 캐스트 할 수 있습니다.

let anyVar: any;
let unknownVar: unknown;
let undefinedVar: undefined;

anyVar = unknownVar; // 다운 캐스트
undefinedVar = anyVar; // 업 캐스트

단, never 타입의 변수에 any 타입의 값을 할당할 수는 없습니다.

let neverVar = anyVar; // ❌

객체 타입의 호환성

객체 타입은 프로퍼티를 기준으로 다른 객체 타입들과 슈퍼-서브 타입 관계를 갖습니다.

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; // ❌ 

Dog 타입이 더 많은 프로퍼티를 정의하고 있어 슈퍼타입처럼 보일 수 있지만 Animal 타입이 Dog 타입의 슈퍼타입입니다.

TypeScript의 타입 호환성은 구조적 서브 타이핑(subtyping)을 기반으로 합니다. 구조적 타이핑이란 오직 멤버만으로 타입을 관계시키는 방식입니다. 명목적 타이핑(nominal typing) 과는 대조적입니다. TypeScript의 구조적 타입 시스템의 기본 규칙은 y가 최소한 x와 동일한 멤버를 가지고 있다면 x와 y는 호환된다는 것입니다. - TypeScript 공식 문서 - 타입 호환성

타입스크립트는 프로퍼티를 기준으로 타입을 정의하는 구조적 타입 시스템을 따릅니다. Animal 타입은 name과 color 프로퍼티를 가지는 타입입니다. Dog 타입은 name과 color 프로퍼티, 추가로 breed 프로퍼티를 가집니다.

Dog 타입의 객체는 Animal 타입의 name과 color 프로퍼티를 반드시 가집니다. 따라서 Animal 타입에도 해당하는 객체입니다. 반대로 Animal 타입은 Dog 타입의 breed 프로퍼티를 가지고 있지 않으므로 Dog 타입에는 해당하지 않습니다.

집합의 관점에서 타입스크립트 바라보기

초과 프로퍼티 검사

Book 타입으로 정의된 변수에 ProgrammingBook 타입으로 보이는 초기값을 설정하면 오류가 발생합니다.

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

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

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

Book 타입은 ProgrammingBook 타입의 슈퍼타입입니다. 이것은 업캐스팅에 해당되는데 오류가 발생합니다.

그 이유는 ‘초과 프로퍼티 검사’가 발동했기 때문입니다. 초과 프로퍼티 검사란 변수를 객체 리터럴로 초기화 할 때 발동하는 타입스크립트의 특수한 기능입니다. 이 기능은 타입에 정의된 프로퍼티 외의 다른 초과된 프로퍼티를 갖는 객체를 변수에 할당할 수 없도록 막습니다.

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

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

let book3: Book = programmingBook;

Reference

profile
차근차근

0개의 댓글