본문으로 건너뛰기

3장 유니언과 리터럴

타입스크립트가 추론을 수행하는 두 가지 핵심 개념은 다음과 같다.

  • 유니언: 값에 허용된 타입을 두 개 이상의 가능한 타입으로 확장하는 것
  • 내로잉: 값에 허용된 타입이 하나 이상의 가능한 타입이 되지 않도록 좁히는 것

3.1 유니언 타입

유니언 타입

  • 값이 정확히 어떤 타입인지 모르지만 두 개 이상의 옵션 중 하나의 타입
  • 가능한 값 또는 구성 요소 사이에 | 연산자를 사용해서 나타냄

3.1.1 유니언 타입 선언

변수의 초깃값이 있더라도 변수에 대한 명시적 타입 애너테이션을 제공하는 것이 유용할 때 유니언 타입을 사용한다.

다음 예제에서 thinker의 초깃값은 null이지만 잠재적으로 null 대신 string이 될 수 있음을 알려준다.

let thinker: string | null = null;

if (Math.random() > 0.5) {
thinker = 'Susanne Langer';
}

3.1.2 유니언 속성

값이 유니언 타입일 때 타입스크립트는 유니언으로 선언한 모든 가능한 타입에 존재하는 멤버 속성에만 접근할 수 있다.

내로잉: 유니언 타입으로 정의된 여러 타입 중 하나의 타입으로 된 값의 속성을 사용하기 위해 코드에서 값이 보다 구체적인 타입 중 하나라는 것을 타입스크립트에 알리는 과정

3.2 내로잉

내로잉: 값이 정의, 선언 혹은 이전에 유추된 것보다 더 구체적인 타입임을 코드에서 유추하는 것

타입 가드: 타입을 좁히는 데 사용할 수 있는 논리적 검사

3.2.1 값 할당을 통한 내로잉

변수에 값을 직접 할당하면 타입스크립트는 변수의 타입을 할당된 값의 타입으로 좁힌다.

변수에 유니언 타입 애너테이션이 명시되고 초깃값이 주어질 때 값 할당 내로잉이 작동한다. 타입스크립트는 변수가 나중에 유니언 타입으로 선언된 타입 중 하나의 값을 받을 수 있지만, 처음에는 초기에 할당된 값의 타입으로 시작한다는 것을 이해한다.

3.2.2 조건 검사를 통한 내로잉

일반적으로 타입스크립트에서는 변수가 알려진 값과 같은지 확인하는 if 문을 통해 변수의 값을 좁히는 방법을 사용한다. 타입스크립트는 if 문 내에서 변수가 알려진 값과 동일한 타입인지 확인한다.

조건부 로직으로 내로잉할 때, 타입스크립트 타입 검사 로직은 자바스크립트 코딩 패턴을 미러링해서 구현한다. 만약 변수가 여러 타입 중 하나라면, 일반적으로 필요한 타입과 관련된 검사를 한다.

let scientist = Math.random() > 0.5 ? 'Rosalind Franklin' : 51;

if (scientist === 'Rosalind Franklin') {
scientist.toUpperCase(); // OK
}

scientist.toUpperCase(); // ERROR

3.2.3 typeof 검사를 통한 내로잉

타입스크립트는 직접 값을 확인해 타입을 좁히기도 하지만, typeof 연산자를 사용할 수도 있다.

let researcher = Math.random() > 0.5 ? 'Rosalind Franklin' : 51;

if (typeof researcher === 'string') {
researcher.toUpperCase();
}

if (!(typeof researcher === 'string')) {
researcher.toFixed();
} else {
researcher.toUpperCase();
}

typeof researcher === 'string' ? researcher.toUpperCase() : researcher.toFixed();

3.3 리터럴 타입

리터럴 타입: 원시 타입 값 중 어떤 것이 아닌 특정 원싯값으로 알려진 타입이 리터럴 타입이다.

const philosopher = 'HyPatia';

원시 타입 string은 존재할 수 있는 모든 가능한 문자열의 집합을 나타낸다. 하지만 리터럴 타입인 “Hypatia”는 하나의 문자열만 나타낸다.

각 원시 타입은 해당 타입이 가질 수 있는 가능한 모든 리터럴 값의 전체 조합으로 생각할 수 있다. 즉, 원시 타입은 해당 타입의 가능한 모든 리터럴 값의 집합이다. boolean, null, undefined 타입 외에 number, string과 같은 모든 원시 타입에는 무한한 수의 리터럴 타입이 있다.

유니언 타입 애너테이션에서는 리터럴과 원시 타입을 섞어서 사용할 수 있다.

노트
let philosopher: string = 'HyPatia'; // string 타입
const philosopher = 'HyPatia'; // "HyPatia" 리터럴 타입
  • let: 변수를 선언하고 나중에 값을 변경할 수 있다. 명시적인 타입 어노테이션을 사용해서 변수의 타입을 지정할 수 있다.
  • const: 값을 변경할 수 없는 상수 변수를 선언한다. 타입을 명시적으로 지정하지 않아도 TypeScript가 할당된 값으로부터 타입을 추론한다.

3.3.1 리터럴 할당 가능성

리터럴 타입은 그 값이 해당하는 원시 타입에 할당할 수 있다. 모든 특정 리터럴 문자열은 여전히 string 타입이기 때문이다.

3.4 엄격한 null 검사

엄격한 null 검사: 리터럴로 좁혀진 유니언의 힘은 타입 시스템 영역인 ‘잠재적으로 정의되지 않은 undefined 값’으로 작업할 때 두드러진다.

3.4.1 십억 달러의 실수

십억 달러의 실수: 다른 타입이 필요한 위치에서 null 값을 사용하도록 허용하는 많은 타입 시스템을 가리키는 업계 용어

엄격한 null 검사가 없는 언어에서는 다음 예제 코드처럼 string 타입 변수에 null을 할당하는 것이 허용된다.

const firstName: string = null;

strictNullChecks 옵션은 엄격한 null 검사를 활성화할지 여부를 결정한다. strictNullChecks를 비활성화하면 코드의 모든 타입에 | null | undefined를 추가해야 모든 변수가 null 또는 undefined를 할당할 수 있다. 엄격한 null 검사를 활성화해야만 코드가 null 또는 undefined 값으로 인한 오류로부터 안전한지 여부를 쉽게 파악할 수 있다. 타입스크립트의 모범 사례는 일반적으로 엄격한 null 검사를 활성화하는 것이다.

3.4.2 참 검사를 통한 내로잉

타입스크립트는 잠재적인 값 중 truthy로 확인된 일부에 한해서만 변수의 타입을 좁힐 수 있다.

3.5 타입 별칭

타입 별칭: type 새로운 이름 = 타입 형태를 갖고 있다.

type RawData = boolean | number | string | null | undefined;

let rawDataFirst: RawData;
let rawDataSecond: RawData;
let rawDataThrid: RawData;

3.5.1 타입 별칭은 자바스크립트가 아닙니다

타입 별칭은 타입 애너테이션처럼 자바스크립트로 컴파일되지 않는다. 순전히 타입스크립트 타입 시스템에만 존재한다. 타입 별칭은 순전히 타입 시스템에만 존재하므로 런타임 코드에서는 참조할 수 없다. 타입스크립트는 런타임에 존재하지 않는 항목에 접근하려고 하면 타입 오류로 알려준다.

3.5.2 타입 별칭 결합

타입 별칭은 다른 타입 별칭을 참조할 수 있다. 유니언 타입인 타입 별칭 내에 또 다른 유니언 타입인 타입 별칭을 포함하고 있다면 다른 타입 별칭을 참조하는 것이 유용하다.

3.6 마치며

타입스크립트의 유니언과 리터럴 타입, 구조화된 코드에서 타입 시스템에 더 구체적인 타입을 유추하는 방법에 대해 살펴봤다.