타입스크립트의 타입 추론이 어떻게 동작하는지 이해하기 위한 핵심 개념이다
타입스크립트는 정밀하기보다 일반적으로 타입을 추론한다
let 으로 값을 바꿀 수 있는 변수를 선언하면
해당 변수의 타입이 리터럴 값에서 기본 타입으로 넓혀진다
let x = 'x' // string
let three = 3 // number
불변 값을 사용하면 다른 결과가 나온다
const x = 'x' // 'x'
const three = 3 // 3
let 을 사용하더라도 타입을 명시하면 타입이 넓어지지 않도록 할 수 있다
null 또는 undefined 로 초기화된 변수는 any 타입으로 확장된다
any 타입으로 확장된 변수가 선언 범위를 벗어나면
타입스크립트는 확실한 (좁은) 타입을 다시 할당한다
-> 벗어나기 전까지는 타입을 다시 할당하지 않는다
function x() {
let a = null // any
a = 'a' // any
return a
}
x() // string
타입이 넓혀지는 것을 방지하는 const 타입을 제공한다
const 를 사용하면 타입 넓히기가 중지되면 멤버들까지 readonly 가 된다
-> 중첩된 자료구조에도 재귀적으로 적용된다
let a = [1, {x: 2}] /// ({number | {x: number}})[]
let b = [1, {x: 2}] as const // readonly [1, {readonly x: 2}]
가능한 좁은 타입으로 추론하길 원한다면 as const 를 이용하는 편이 좋다
타입 스크립트가 한 객체 타입을 다른 객체 타입에 할당할 수 있는지 확인할 때
타입 넓히기를 이용한다
객체 타입과 그 멤버들은 공변 관계
타입스크립트가 추가 확인을 하지 않고 이 규칙만을 적용하면 문제가 발생할 수 있다
객체 멤버의 철자를 틀리거나 할 경우
타입스크립트는 틀린 철자에 대해 존재하지 않음과 함께
기존 멤버 중 추천해준다
이는 타입스크립트가 초과 프로퍼티를 확인하기 때문에 가능하다
신선한 객체 리터럴 타입 T 를 다른 타입 U 에 할당하는 상황에서
T 가 U 에 존재하지 않는 프로퍼티를 가지고 있다면
타입스크립트는 이는 에러로 처리한다
신선한 객체 리터럴 타입: 객체 리터럴로부터 추론한 타입
객체 리터럴 타입 어셔선을 사용하거나 변수로 할당되면
신선한 객체 리터럴 타입은 일반 객체 타입으로 넓어지면서
신선함이 사라진다
타입스크립트는 심벌 수행의 일종인 흐름 기반 타입 추론을 수행한다
타입 검사기는 typeof, instanceof, in 등의 타입 질의 뿐 아니라
if, ?, ||, switch 같은 제어 흐름 문장까지 고려하여
타입을 정제한다
이는 아주 일부 언어에서만 지원하는 기능
type UserTextEvent = { value: string, target: HTMLInputElement }
type UserMouseEvent = { value: [number, number], target: HTMLInputElement }
type UserEvent = UserTextEvent | UserMouseEvent
function handle(event: UserEvent) {
if (typeof event.value === 'string') {
event.value // string
event.target // HTMLInputElement | HTMLElement
return
}
event.value // [number, number]
event.target // HTMLElement
}
event.value 는 잘 정제되었지만 event.target 은 그렇지 않았다
UserEvent 타입은 UserTextEvent, UserMouseEvent 뿐만 아니라
UserTextEvent | UserMouseEvent 를 받을 수도 있다
유니온 멤버가 중복될 수 있으므로 타입스크립트는 유니온의 어떤 타입에 해당하는지 조금 더 안정적으로 파악할 수 있어야 한다
각각의 경우를 태그하는 방식으로 이 문제를 해결할 수 있다
type UserTextEvent = { type: 'TextEvent', value: string, target: HTMLInputElement }
type UserMouseEvent = { type: 'MouseEvent', value: [number, number], target: HTMLInputElement }
type UserEvent = UserTextEvent | UserMouseEvent
function handle(event: UserEvent) {
if (event.type === 'TextEvent') {
event.value // string
event.target // HTMLInputElement
return
}
event.value // string
event.target // HTMLInputElement | HTMLElement
}
철저 검사(exaustiveness checking)라고도 불리는 종합성은
필요한 모든 상황을 제대로 처리했는지 타입 검사기가 검사하는 것을 말한다
타입스크립트는 다양한 상황의 모든 가능성을 확인하며
빠진 상황이 있다면 이를 경고한다