종합성(Totality) 철저 검사라고도 불리는 종합성은 필요한 모든 상황을 제대로 처리했는지 타입 검사기가 검사하는 기능입니다. 종합성은 하스켈, 오캐멀 등 패턴 매칭을 사용하는 언어에서 차용한 기능입니다. TypeScript는 다양한 상황의 모든 가능성을 확인하며, 빠진 상황이 있다면 이를 경고합니다. 실제로 일어날 버그를 방지하는 데 아주 도움 되는 기능입니다. 아래 예를 살펴보겠습니다. type DayOfTheWeek = 'Mon' | 'Tue' | 'Wed' | 'Thu' | 'Fri' type Day = DayOfTheWeek | 'Sat' | 'Sun' function getNextDay(d: DayOfTheWeek): Day { // 함수에 끝 return 문이 없으며 반환 형식에 'und..
차별된 유니온 타입 TypeScript는 JavaScript가 어떻게 동작하는지 잘 이해하며, 마치 프로그래머가 머리로 프로그램을 추적하듯이 코드로부터 타입을 정제해 낼 수 있습니다. 예를 들어 응용 프로그램의 커스텀 이벤트 시스템을 만든다고 가정합니다. 먼저 몇 가지 이벤트 타입과 이벤트들을 처리할 함수를 정의합니다. KeyBoardEvent는 키보드 이벤트를, MouseControlEvent는 마우스 이벤트를 가리킵니다. type KeyBoardEvent = {value: string} type MouseControlEvent = {value: [number, number]} type FirstEvent = KeyBoardEvent | MouseControlEvent function handle(ev..
정제 TypeScript는 심벌 수행(symbolic execution)의 일종인 흐름 기반 타입 추론을 수행합니다. 즉, 타입 검사기는 typeof, instanceof, in 등의 타입 질의뿐 아니라, 마치 프로그래머가 코드를 읽듯 if, ?, ||, switch 같은 제어 흐름 문장까지 고려하여 타입을 정제(refinement)합니다. 이는 타입 검사기에 제공되는 아주 유용한 기능으로 오직 일부의 언어에서만 지원합니다. 예를 살펴보겠습니다. TypeScript에 CSS 규칙을 정의하는 내장 API가 있고 동료 개발자가 HTML 요소의 너비(width)를 설정하려 한다고 가정해 봅시다. 너비를 전달하면 이를 파싱하고 검증할 것입니다. 먼저 CSS 문자열을 값(value)과 단위(unit)로 파싱 하는..
초과 프로퍼티 TypeScript가 한 객체 타입을 다른 객체 타입에 할당할 수 있는지 확인할 때도 타입 넓히기를 이용합니다. 객체 타입과 그 멤버들은 공변 관계라는 규칙만을 적용하면 문제가 발생할 수 있습니다. 예를 들어 다음처럼 클래스에 전달해 그 내부 상태를 설정하는 용도의 Options라는 객체가 있다고 해보겠습니다. type Options = { baseURL: string cacheSize?: number tier?: 'prod' | 'dev' } class API { constructor(private options: Options){} } new API({ baseURL: 'https://cozy-linda.tistory.com', tier: 'prod' }) 옵션의 철자가 틀리면 어떤 일..
타입 넓히기(type widening) 타입 넓히기는 TypeScript의 타입 추론이 어떻게 동작하는지 이해하는 데 필요한 핵심 개념입니다. TypeScript는 타입을 정밀하게 추론하기보다는 일반적으로 추론합니다. (덕분에 프로그래머의 삶이 편해집니다) let이나 var로 값을 바꿀 수 있는 변수를 선언하면 그 변수의 타입이 리터럴 값에서 리터럴 값이 속한 기본 타입으로 넓혀집니다. let a = 'x' // let a: string let b = 2 // let b: number var c = true // boolean const d = { x: 4} // (property) x: number enum E {A, B, C} let e = E.A // let e: E 값을 바꿀 수 없는 변수에선 상..
할당성(assignability) 서브 타입과 슈퍼 타입의 관계는 모든 정적 타입 언어에서 중요한 개념입니다. 또한 할당성(assignability)이 어떻게 동작하는지 이해하는데도 중요합니다. 할당성이란 A라는 타입을 다른 B라는 타입이 필요한 곳에 사용할 수 있는지를 결정하는 TypeScript 규칙을 의미합니다. "A를 B에 할당할 수 있는가?"라는 질문이 발생하면 TypeScript는 다음과 같은 몇 가지 규칙에 따라 처리합니다. 배열, 불(boolean), 숫자, 객체, 함수, 클래스, 클래스 인스턴스, 문자열, 리터럴 타입 등 열거형이 아닌 타입에선 다음의 규칙으로 A를 B에 할당할 수 있는지 결정합니다. A
함수 가변성 몇 가지 예를 살펴보겠습니다. 함수 A가 함수 B와 같거나 적은 수의 매개변수를 가지며 다음을 만족하면, A는 B의 서브 타입입니다. A의 this 타입을 따로 지정하지 않으면 'A의 this 타입 >: B의 this 타입'입니다. 'A의 각 매개변수 >: B의 대응 매개변수'입니다. 'A의 반환 타입 Flower' 형식의 인수는 '(r: Rose) => Rose' 형식의 매개 변수에 할당될 수 없습니다. // 'red' 속성이 'Flower' 형식에 없지만 'Rose' 형식에서 필수입니다.ts(2345) // index.ts(4, 3): 여기서는 'red'이(가) 선언됩니다. clone(roseToFlower) roseToThron은 정상 동작하지만 roseToFlower에서 에러가 발생합..
형태와 배열 가변성 복합 타입의 서브 타입 규칙은 왜 언어마다 다를까요? 이번 글에서 소개하는 복잡한 타입(형태)을 예로 살펴보겠습니다. 일단 응용 프로그램에 사용자를 묘사하는 형태가 있다고 가정하고, 다음처럼 두 가지 타입으로 표현한다고 해봅시다. // 서버로부터 받은 기존 회원 type ExistingMemer = { id: number nickName: string } // 아직 서버에 저장하지 않은 새 회원 type NewMember = { nickName: string } 어떤 동아리의 회원 관리를 하게 되었는데 회원을 삭제하는 코드를 구현하는 역할을 맡았으면 다음과 같이 구현할 수 있습니다. // 서버로부터 받은 기존 회원 type ExistingMemer = { id: number; nick..
가변성 보통 A라는 타입이 V라는 다른 타입의 서브 타입인지 아닌지 쉽게 판단할 수 있습니다. number, string 등의 단순 타입은 위의 흐름도로 확인하거나 자체적으로 쉽게 추론할 수 있습니다. (예: number는 number | string 유니온에 포함되므로 number | string의 서브 타입입니다.) 매개변수화된(제네릭) 타입 등 복합 타입에선 이 문제가 더 복잡해집니다. 아래의 상황을 살펴보겠습니다. Array는 어떤 상황에서 Array의 서브 타입이 될까요? 형태 A는 어떤 상황에서 다른 형태 B의 서브 타입이 될까요? 함수 (a: A) => B는 어떤 상황에서 다른 함수 (c: C) => D의 서브 타입이 될까요? 다른 타입을 포함하는 타입(Array처럼 타입 매개변수를 갖거나,..