ts-for-jsdev
  • 자바스크립트 개발자를 위한 타입스크립트
  • 들어가며
  • 01 타입스크립트 소개
    • 1.0 들어가며
    • 1.1 정적 타입 분석
    • 1.2 왜 타입스크립트인가
    • 1.3 타입스크립트의 구성요소
    • 1.4 타입스크립트의 역사
  • 02 ECMAScript
    • 2.0 ECMAScript
    • 2.1 블록 수준 스코프
      • 2.1.1 let을 이용한 선언
      • 2.2.2 const를 이용한 선언
      • 2.2.3 스코프 베스트 프랙티스
    • 2.2 객체와 배열
      • 2.2.1 비구조화 할당
      • 2.2.2 나머지 연산자와 전개 연산자
      • 2.2.3 객체 리터럴 변경사항
    • 2.3 함수
      • 2.3.1 기본 매개변수
      • 2.3.2 화살표 함수
    • 2.4 템플릿 리터럴
      • 2.4.1 멀티라인 문자열
      • 2.4.2 문자열 치환
    • 2.5 원소 순회
      • 2.5.1 forEach 메소드
      • 2.5.2 for-of 문법
      • 2.5.3 이터레이터 프로토콜
      • 2.5.4 이터러블 프로토콜
    • 2.6 비동기 처리
      • 2.6.1 프로미스
      • 2.6.2 Async / Await
    • 2.7 맺으며
  • 03 타입스크립트 기초 문법
    • 3.0 타입스크립트 기초 문법
    • 3.1 기본 타입
    • 3.2 배열과 튜플
    • 3.3 객체
    • 3.4 타입 별칭
    • 3.5 함수
    • 3.6 제너릭
    • 3.7 유니온 타입
    • 3.8 인터섹션 타입
    • 3.9 열거형
  • 04 인터페이스와 클래스
    • 4.0 들어가며
    • 4.1 인터페이스 기초
    • 4.2 색인 가능 타입
    • 4.3 인터페이스 확장
    • 4.4 클래스
    • 4.5 클래스 확장
    • 4.6 클래스 심화
      • 4.6.1 스태틱 멤버
      • 4.6.2 접근 제어자
      • 4.6.3 접근자
      • 4.6.4 추상 클래스
    • 4.7 인터페이스와 클래스의 관계
    • 4.8 맺으며
  • 05 타입의 호환성
    • 5.0 들어가며
    • 5.1 기본 타입의 호환성
    • 5.2 객체 타입의 호환성
    • 5.3 함수 타입의 호환성
    • 5.4 클래스의 호환성
    • 5.5 제너릭의 호환성
    • 5.6 열거형의 호환성
    • 5.7 맺으며
  • 06 타입 시스템 심화
    • 6.0 들어가며
    • 6.1 타입 좁히기
    • 6.2 타입 추론
    • 6.3 타입 단언
    • 6.4 집합으로서의 타입
    • 6.5 서로소 유니온 타입
    • 6.6 맺으며
  • 07 고급 타입
    • 들어가며 (7월 2일 공개)
  • 08 모듈과 네임스페이스
    • 들어가며 (7월 16일 공개)
  • 09 실제 프로젝트에서 사용하기
    • 들어가며 (7월 30일 공개)
  • 10 유용한 라이브러리 소개
    • 들어가며 (8월 13일 공개)
    • 맺으며
  • 부록 II : 자바스크립트 언어 생태계
    • ECMAScript 언어 표준과 TC39
    • TC39 프로세스
    • 실제 예시 - Array.prototype.includes
    • 타입스크립트와 ECMAScript
Powered by GitBook
On this page
  • 최적의 공통 타입
  • 문맥 상의 타입
  1. 06 타입 시스템 심화

6.2 타입 추론

컴파일러는 타입 추론을 통해 명시적인 타입 표기 없이도 타입 정보를 이해할 수 있다.

지금까지의 이 책의 모든 코드 예제는 타입을 명시적으로 표기해줬다. 하지만 3장의 도입부에서 언급했듯, 타입스크립트는 타입 추론을 지원한다. 프로그래머가 타입 표기를 적지 않아도 값의 타입을 추론해 낼 수 있는 것이다. 다음은 타입 표기를 명시적으로 적어준 버전이다.

let x: number = 3;

하지만 위의 코드에서 타입 표기를 없애더라도 타입스크립트는 3이라는 값으로부터 x가 number 타입일 것이라 추론해낸다.

let x = 3; // number 타입으로 추론

한 편, let 대신 const 를 사용하면 어떨까? 타입스크립트는 이처럼 재할당이 불가능한 경우에는 변수의 타입을 보다 구체적인 숫자 리터럴 타입으로 추론한다. 이러한 속성은 인터페이스나 클래스의 readonly 속성에도 비슷하게 적용된다.

const x = 3; // 3 타입으로 추론

물론 의도한 타입이 할당하는 값만으로는 드러나지 않는 경우엔 여전히 타입 표기가 필요하다. 예를 들어 아래의 코드에서 타입 표기 없이 컴파일러가 1 | 3 이라는 타입을 추론하긴 불가능 할 것이다.

const oneOrThree: 1 | 3 = 3;

최적의 공통 타입

하나의 값에 대한 타입 추론은 단순하다. 그렇다면 여러 값이 연관된 타입을 추론할 때는 어떨까? 다음 코드를 보자.

interface Animal {
  legs: number;
}

interface Dog extends Animal {
  bark(): void;
}

interface Cat extends Animal {
  meow(): void;
}

let dog: Dog;
let cat: Cat;
const dogAndCat = [dog, cat]; // ??

dogAndCat은 Dog 타입의 원소와 Cat 타입의 원소를 갖는 배열이다. 이러한 배열의 타입은 어떻게 추론해야 할까?

이런 상황에서 타입스크립트는 최적 공통 타입(best common type)이란 접근법을 사용한다. 원리는 간단한데, 모든 가능한 타입의 유니온 타입을 사용하는 것이다. 예를 들어 위 dogAndCat의 타입을 Array<Dog | Cat> 으로 추론하는 식이다.

이 때 두 인터페이스 모두 Animal 인터페이스를 호환받았으니 Array<Animal>로 추론하면 되는 것 아닐지 궁금해 할 수 있다. 그렇게 동작하지 않는 이유를 이해하기 위해 다음 예제를 보자.

interface Camel extends Animal {
  humps: number;
}

function getSoundFunction(dogOrCat: Dog | Cat) {
  if ('meow' in dogOrCat) {
    return dogOrCat.meow;
  } else {
    return dogOrCat.bark;
  }
}

타입스크립트는 최적 공통 타입에 따라 dogAndCat를 Array<Dog | Cat> 타입으로 추론한다. 따라서 다음 코드는 실제로 타입 검사를 통과한다. 아래 코드는 실제로 문제가 생길 여지가 없으므로 이는 바람직한 동작이다.

dogAndCat.map(dogOrCat => getSoundFunction(dogOrCat));

하지만 만약 배열의 타입을 Array<Animal>로 추론한다면 어떻게 될지 생각해보자. getSoundFunction 함수는 Camel 타입을 인자로 받지 않는다. 그 때문에, dogAndCat 내에는 Camel 타입 값이 존재하지 않음에도 위의 코드에선 타입 에러가 날 것이다.

이런 불편함을 막고자, 최적 공통 타입에서는 타입 추론에 사용된 값들의 타입(이 경우엔 Dog와 Cat)만을 재료로 사용한다. dogAndCat이 Array<Animal> 타입을 갖길 원한다면 타입 추론에 의존하는 대신 명시적으로 타입 표기를 해 주면 된다.

문맥 상의 타입

할당이 일어날 때, 타입 추론은 할당 받는 값(왼쪽 항)의 타입 뿐만 아니라 할당하는 값(오른쪽 항)의 타입에 대해서도 일어난다. 이렇게 추론된 타입을 문맥 상의 타입(contextual type)이라 부른다.

window.onmousedown = function(mouseEvent) {
  console.log(mouseEvent.a);
};

이 때, Window 인터페이스의 onmousedown 속성은 아래와 같이 정의되어 있다.

interface MouseEvent {
  /* ... */
  /* button 속성 없음! */
}

interface Window {
  /* ... */
  onmousedown: (event: MouseEvent) => void;
}

따라서 타입스크립트는 우변의 함수가 (event: MouseEvent) => void 타입일 것이라고 추론한다. 이 때 함수 내부에서 event.a 속성에 접근하는데, a 속성은 MouseEvent 타입에 존재하지 않으므로 타입 에러가 발생한다.

만약 타입 표기가 주어졌다면 문맥 상의 타입은 무시된다. 예를 들어, 다음과 같이 mouseEvent 매개변수의 타입을 표기해주면 위의 에러는 사라진다.

window.onmousedown = function(mouseEvent: any) {
  console.log(mouseEvent.a);
};
Previous6.1 타입 좁히기Next6.3 타입 단언

Last updated 6 years ago