유튜브에 공개된 네이버 사내 기술 교류 행사인 NAVER ENGINEERING DAY 2024(5월)에서 발표되었던 세션을 보고 타입스크립트의 타입 시스템에 대한 이해가 깊어졌습니다. 해당 발표 영상을 보고 추후 다시 찾아볼 수 있도록 정리한 내용입니다. 본문에는 제 나름 대로의 해석이 추가되어 오해의 여지가 있을 수 있습니다.
발표 영상: infer, never만 보면 두려워지는 당신을 위한 고급 TypeScript
타입이론과 TypeScript
러셀의 역설(Russell's Paradox)의 발견으로 수학의 근간인 집합론이 흔들리며 함수에 대한 고찰도 이루어졌다.
기존 집합 사이의 관계로만 생각했던 함수에 대한 고찰을 시작은 람다 대수(Lambda Calculus)의 고안을 이끌었고 이를 고도화 하던 중 타입의 개념이 도입되었다. 관련 내용은 해당 문서에 상세히 나와있다. https://plato.stanford.edu/entries/type-theory/
놀랍게도 TypeScript는 1.8까지 공식문서가 존재했으나 Microsoft가 문서 유지보수를 포기하며 tsc의 구현이 곧 TypesScript의 스펙이 되었다.
해당 영상은 tsc를 블랙박스로 간주하고 수학적 일관성과 실험을 근거로 서술한다.
기초 타입 추론
슈퍼타입과 서브타입
영상에서는 타입을 `어떤 심볼(Symbol, ≒ 변수명)에 엮인(binded) 메모리 공간에 존재할 수 있는 값(value)의 집합과 그 값들이 가질 수 있는 성질(properties)`로 정의한다.
`3.141592 : number` 이와 같은 표기는 `값 3.141592는 타입 number에 속한다.`로 이해할 수 있다.
$$\{\text{x}:\text{number};\thinspace\text{y}?:\text{string} \}\lesssim\{\text{x}:\text{number}\}$$
타입 $\{\text{x}:\text{number};\thinspace\text{y}?:\text{string}\}$은 타입 $\{\text{x}:\text{number}\}$에 속한다.
타입 $\{\text{x}:\text{number};\thinspace\text{y}?:\text{string} \}$은 타입 $\{\text{x}:\text{number}\}$의 서브타입이다.
타입 $\{\text{x}:\text{number}\}$은 타입 $\{\text{x}:\text{number};\thinspace\text{y}?:\text{string}\}$의 슈퍼타입이다.
`타입 B가 가지는 모든 속성을 타입 A가 가지면, 타입 A는 B의 서브타입이다.`
`서브타입은 슈퍼타입의 모든 속성을 갖는다.`
타입간의 관계와 대입
타입은 4가지 관계를 가질 수 있다.
- $\text{a} \gtrsim \text{b}$: a가 b의 슈퍼타입이다. ( $\text{number} \gtrsim 42$ )
- $\text{a} \lesssim \text{b}$: a가 b의 서브타입이다. ( $\text{string} \lesssim \text{string \textbar number}$ )
- $\text{a} \simeq \text{b}$: a와 b가 서로 서브타입이다. ( $\{\text{x}?:\text{number}\}\simeq \{\text{x}:\text{number \textbar \text{undefined}}\}$ )
- $\text{a} \not\simeq \text{b}$: 서로 관계가 없다. ( $\text{number} \not\simeq \{\text{x}:\text{string}\}$ )
작은 타입을 큰 타입에 대입할 수 있다.
`서브타입을 슈퍼타입에 대입할 수 있다.`
원시 타입(Primitive Type)
- boolean, number, string, symbol, null, undefined
- 공리적으로 정의
- 상호 무관계
- null을 제외한 모든 원시 타입은 `typeof`연산 결과 해당 타입의 이름이 나온다.
리터럴 타입(Literal Type)
- 어떤 타입에 속한 값 하나만으로 구성하는 타입
- 본래 타입의 서브타입으로 간주
- as const 키워드로 특정 값을 리터럴 타입으로 선언할 수 있다.
const x = 6; // x: number (원시 타입으로 추론)
const y = 6 as const; // y: 6 (리터럴)
객체 타입(Object Type)
- 타입 L과 R에서 타입 L의 모든 속성 P에 대하여 $\text{L[P]}\gtrsim \text{R[P]}$이면 $\text{L}\gtrsim\text{R}$이다.
- 이때 객체의 타입은 속성의 타입에 대해 `공변적(Covariant)`이라 한다.
배열/튜플 타입(Array/Typle Type)
- 객체와 동일하다. 키가 number로 고정되어 있다는 점만 다르다.
- 튜플은 length가 상수 리터럴 타입으로 고정되어있다.
$$\text{a}\lesssim\text{b}\Longrightarrow\text{A[ ]}\lesssim\text{B[ ]}$$
함수 타입(Function Type)
- 반환형과 인자형의 대입 조건을 모두 만족해야한다.
- 반환형에 대해서는 `공변적(Covariant)`
- $\text{A}\lesssim\text{B}\Longrightarrow\text{X}\mapsto\text{A}\lesssim\text{X}\mapsto\text{B}$
- 인자형이 같은 두 함수에서 A가 B의 서브타입이라면 A를 반환하는 함수가 B를 반환하는 함수의 서브타입이다.
- 함수의 타입 관계는 반환형의 타입관계와 같은 방향이다. (공변적이다.)
- 반환값은 rvalue로 사용되기 때문에 함수의 타입과 반환형의 타입은 공변적이다.
- 인자형에 대해서는 `반변적(Contravariant)`
- $\text{A}\lesssim\text{B}\Longrightarrow\text{A}\mapsto\text{X}\gtrsim\text{B}\mapsto\text{X}$
- 반환형이 같은 두 함수에서 A가 B의 서브타입이라면 A를 인자로 갖는 함수는 B를 인자로 갖는 함수의 슈퍼타입이다.
- 인자의 수가 불일치 하는 경우
- 인자가 적은 함수를 인자가 많은 함수에 대입할 수 있다.
- 인자가 적은 함수가 서브타입, 인자가 많은 함수가 슈퍼타입이 된다.
- 인터페이스(슈퍼타입, 인자 많은 함수)로 들어오는 넘치는 인자는 구현체(서브타입, 인자 적은 함수)에서 버리면 그만이다.
특수 타입
Javascript에는 존재하지 않으나 TypeScript에서 타입상으로만 존재하는 타입
- never, unknown, any, void
- never & unknown
- $\forall_T:\text{never}\lesssim\text{T}\lesssim\text{unknown}$
- `never`는 가장 좁은 타입이다.
- 모든 타입의 서브타입이다.
- 공집합과 비슷하다.
- 어떤 타입도 대입할 수 없다.
- 타입 에러를 고의적으로 발생시킬 때에도 사용(제네릭에서 사용)
- `unknown`은 가장 넓은 타입이다.
- 모든 타입의 슈퍼타입이다.
- any, unknown을 제외한 어떠한 곳에도 대입이 불가능하다.
- 모든 값을 받을 수는 있지만 대입할 수는 없다.
- any
- $\forall_{\text{T}\not=\text{never}}:\text{T}\simeq\text{any},\,\text{never}\lesssim\text{any}$
- never를 제외한 모든 타입과 서로 서브타입이다.
- never를 제외한 모든 타입에 대입 가능하며 모든 타입을 받을 수 있다.
const thisIsNever: never = undefined; // ERROR: never는 모든 타입의 서브타입이다.
const thisIsUnknown: unknown = 0; // unknown은 모든 타입의 슈퍼타입이다.
퀴즈
// 다음 중 이론 상 가장 넓은 함수의 타입은? (슈퍼타입은?)
type A = (...args: unknown[]) => unknow;
type B = (...args: never[]) => unknow;
type C = (...args: any[]) => any;
type D = (...args: void[]) => never;
`B`는 모든 함수의 슈퍼타입이다.
함수의 타입은 반환형과 공변적, 인자형과 반변적이므로 반환형은 넓고, 인자형은 좁을 수록 함수의 타입은 넓어진다.
반환형이 가장 넓은 타입인`unknown`이고 인자형이 가장 좁은 타입인 `never`인 `B`가 가장 넓은 함수 타입이다.
고급 타입 추론
33.27
'Languages > JS∕TS' 카테고리의 다른 글
[TS] How to get type from property of objects in array (0) | 2022.07.02 |
---|---|
[TS] interface, impliments, extends 예시 (0) | 2022.02.23 |
[TS] 기본 자료형 (0) | 2022.02.22 |
[코어 자바스크립트] 메모리 동작과 mutability, immutability (0) | 2021.12.25 |
[JS] 호이스팅과 TDZ (0) | 2021.10.05 |