Typescript: 타입 넓히기

Lumpen·2024년 8월 12일
0

Typescript

목록 보기
21/23

Type widening

타입스크립트의 타입 추론이 어떻게 동작하는지 이해하기 위한 핵심 개념이다
타입스크립트는 정밀하기보다 일반적으로 타입을 추론한다

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 타입을 제공한다
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)라고도 불리는 종합성은
필요한 모든 상황을 제대로 처리했는지 타입 검사기가 검사하는 것을 말한다
타입스크립트는 다양한 상황의 모든 가능성을 확인하며
빠진 상황이 있다면 이를 경고한다

profile
떠돌이 생활을 하는. 실업자, 부랑 생활을 하는

0개의 댓글