[TypeScript] 섹션4. 타입스크립트 이해하기(2)

jaehoon ahn·2025년 2월 4일

TypeScript

목록 보기
5/14
post-thumbnail

객체 타입의 호환성

⇒ 어떤 객체 타입을 다른 객체타입으로 취급해도 괜찮은지에 대한 문제

예시

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; // 업캐스팅
dog = animal; // 에러발생, 다운캐스팅

Animal 타입이 Dog 타입을 포함하는 슈퍼타입이라는 의미

Dog는 Animal 타입의 서브 타입을 의미한다.

객체들은 property를 기준으로 관계를 가진다.

Dog 타입이 Animal 타입의 프로퍼티를 다 가지면서, breed를 더 가지고 있는데, 이렇게 보면 Dog 타입이 슈퍼타입으로 여길 수 있다.

하지만, 타입스크립트는 프로퍼티를 기준으로 타입을 정의하는 구조적 타입 시스템을 따른다.

Dog 타입은 name, color을 가지고 있으므로 Animal 타입의 규칙에도 해당한다.

반대로, Animal 객체는 Dog 타입 객체에 포함되지 않는다(breed가 없음).

⇒ 추가 프로퍼티가 없는 조건이 적은 타입이 슈퍼타입이 된다.

초과 프로퍼티 검사

book = programmingBook;

위에서는 업캐스팅이 일어나서 대입이 가능하지만, 아래 코드를 보면 skill이라는 프로퍼티를 넣으면 정의되지 않았다고 하면서 오류가 발생한다. 하나는 되고 하나는 안되는 상황이 발생한다.

// 초과 프로퍼티 검사
type Book = {
  name: string;
  price: number;
};

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

이는 실제 타입에는 정의해 놓지 않은 프로퍼티가 있다면 안되도록 막는 검사가 초과 프로퍼티다.

대수 타입

대수타입이란

⇒ 여러개의 타입을 합성해서 새롭게 만들어낸 타입

⇒ 합집합 타입과 교집합 타입이 존재한다.

1. 합집합 - Union 타입

let a: string | number | boolean;
a = 1;
a = "hello";
a = true;// a에는 number, string, boolean타입 모두 들어갈 수 있다.

Union 타입을 만들 때, “|” 를 이용해서 만들 수 있는 것은 무한대다.

객체 타입들을 이용해서 Union 타입 만들기

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

type Person = {
  name: string;
  language: string;
};
type Union1 = Dog | Person;

let union1: Union1 = { // Dog
  name: "",
  color: "",
};
let union2: Union1 = { // Person
  name: "",
  language: "",
};
let union3: Union1 = {  // 모든 프로퍼티를 넣었을 때
  name: "",
  color: "",
  language: "",
};

let union4: Union1 = { // 오류 발생
  name: "",
};

union1은 Dog 타입의 프로퍼티를, union2는 Person 타입의 프로퍼티를, union3는 두 타입을 모두 충족할 수 있도록 모든 프로퍼티가 포함되어 있다.

하지만, union4에서는 오류가 발생한다.

이는 집합으로 이해하면 쉬운데, 위 그림을 보면 union4는 어느 집합에도 속하지 않기 때문에, 오류가 발생하는 것이다.

2. 교집합 - Intersection 타입

&를 사용한다.

let variable: number & string;

ex) number 타입과 string 타입의 교집합을 갖는 타입

⇒ number 타입과 string 타입은 공집합이므로, never 타입이 된다.

⇒ 즉, 기본 타입들 중에는 서로 공유되거나 겹치는 값들이 없기 떄문에, 교집합 타입은 객체 타입에서 많이 사용된다.

예시

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

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

type Intersection = Dog & Person;

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

교집합은 Dog와 Person에 모두 포함이 되야 하므로, 모든 프로퍼티를 갖고 있는 객체만을 교집합 타입으로 포함될 수 있다.

따라서, 하나라도 프로퍼티를 빼먹으면 포함되지 않는다.

타입 추론

타입 추론이란

타입을 정의하지 않아도 값을 넣어서 초기값을 넣어주면 타입스크립트가 알아서 타입을 추론해주는 것을 의미.

1. 일반적인 변수를 선언하고 초기화 하는 상황

let a = 10;
let b = "hello";
let c = {
  id: 1,
  name: "제노",
  profile: {
    nickname: "zeno",
  },
};

let { id, name, profile } = c;
let [one, two, three] = [1, "hello", true];

⇒ 이는 타입을 추론하는 기준을 변수의 초기값을 토대로 추론하는 것이다.

2. 함수의 반환값 추론

function func() {
  return "hello";
}

⇒ 이는 반환값을 기준으로 추론하는 것이다.

3. 매개변수에 기본값이 설정되어 있는 상황

function func2(message = "hello") {
  return "hello";
}

4. 초기값이 없는 상황(암묵적 any타입, any타입의 진화)

let d; // 초기값 생략 -> any타입으로 추론 => 타입이 변신하듯이 바뀌는 상황을 any타입의 진화라고 부른다.
// 변수를 선언하고 초기값을 넣어주지 않으면 암묵적인 any타입으로 추론됌
// 암묵적 any타입이란?
// 일단 아무런 정보가 없으니 암묵적 any타입으로 추론할게~ 라는 의미
d = 10; // 10이 들어감으로써 number 타입으로 추론
// 10을 넣는 순간 any -> number로 진화
d.toFixed();
d.toUpperCase(); // (x)

d = "hello"; // string을 넣어도 문제가 안됌. 또한 string으로 추론됌
// number -> string으로 진화
d.toUpperCase();
d.toFixed(); // (x)

4.1 암묵적 any 타입과 명시적 any 타입의 차이점

let e: any; // 명시적으로 any타입을 정의하면 다 any타입이므로 진화가 없다.
// 암묵적으로 any를 갖게 되면 타입이 계속해서 진화한다.

5. const

const num = 10;
// 어차피 상수이기 때문에, 10 이외에는 다른 값을 담지 않음. 따라서, number literal 타입 10으로 추론됌
const str = "hello";

6. 여러 타입으로 초기화 하는 경우

let arr = [1, "string"];
// number 와 string의 유니온타입으로 추론됌
// number와 string 타입을 만족해야 하기 때문에 union 타입으로 추론되는 것임

0개의 댓글