thumbnail
union(유니언)과 narrowing(내로잉)
Mar 31, 2023

타입스크립트에 존재하는 두 가지 핵심 추론 개념인 union과 narrowing에 대해 알아보자.

union(유니언)

union은 허용된 타입 값을 두 개 이상의 타입으로 확장하는 것이다. 정확히 어떤 값인지는 모르지만, 여러 타입 중 하나인 것을 알고 있을 때 유용하다.

타입 선언

union은 | 연산자를 이용하여 타입을 구분하여 선언한다. 타입 선언의 순서는 중요하지 않다.

let sec: string | number;

변수의 초깃값이 있음에도 명시적으로 타입 값을 할당해야 할 때 주로 사용한다.

let sec: string | null = null; if (new Date().getSeconds() > 30) { sec = 'Time out'; }

유니언 속성

유니언 타입을 가진 값은 선언한 모든 타입에 존재하는 속성에만 접근할 수 있다. 예를 들어, string | number 타입을 가진 경우 toString() 속성은 사용할 수 있으나 toUpperCase()toExponential() 속성은 사용할 수 없다.

let sec = new Date().getSeconds() > 30 ? 'Time out' : 10; sec.toString(); sec.toUpperCase(); // Error sec.toExponential(); // Error

타입스크립트는 타입이 확실하게 정해져 있지 않은 경우, 어느 하나라도 속하지 않은 속성을 사용하려고 하는 것은 안전하지 않다고 여긴다.


narrowing(내로잉)

narrowing은 이전에 선언된 타입보다 더 구체적으로 유추하여 타입을 좁히는 것이다. 타입을 좁히는 데 사용하는 논리적 검사를 type guard(타입 가드)라고 한다.

typeof 검사

자주 사용하는 타입 가드는 typeof 연산자를 사용하는 것이다. typeof로 해당 값의 타입이 조건과 일치하면 조건문 내의 코드를 실행한다.

let sec = new Date().getSeconds() > 30 ? 'Time out' : 10; if (typeof sec === 'string') { sec.toUpperCase(); // TIME OUT }

else 문이나 삼항 연산자를 사용해 작성할 수도 있다.

let sec = new Date().getSeconds() > 30 ? 'Time out' : 10; if (!(typeof sec === 'string')) { sec.toExponential(); // 1e+1 } else { sec.toUpperCase(); // TIME OUT } typeof sec === 'string' ? sec.toUpperCase() : sec.toExponential();

값 할당

변수에 값을 할당하는 것으로도 타입을 좁힐 수 있다. 아래 코드에서 sec 변수는 초기에 string | number 타입을 선언했지만, 값이 할당된 후 string 타입으로 변한 것을 확인할 수 있다.

let sec: string | number; sec = 'Time out'; // sec: string sec.toUpperCase(); // TIME OUT

Truthiness

Truthiness narrowing은 truthy 값이 if 문이나 && 연산자에서 true로 간주되는 성질을 이용한 것이다. 변수가 string | undefined 타입인 경우, undefined는 항상 falsy 값이므로 if 문 내에서 변수는 string 값이라고 추론한다.

let sec = new Date().getSeconds() > 30 ? 'Time out' : undefined; // if 문 내에서만 string if (sec) { sec.toUpperCase(); // TIME OUT } sec.toUpperCase(); // Error

&& 연산자일 때도 같은 논리이다.

let sec = new Date().getSeconds() > 30 && 'Time out'; if (sec) { sec; // string } else { sec; // false | string }

literal(리터럴) 타입

리터럴 타입은 어떠한 원시 값 중 하나가 아닌, 특정 원시 값을 가진 타입이다. const로 선언한 변수에 값을 직접 할당하면 타입스크립트가 해당 변수를 할당된 리터럴 값으로 유추한다.

const physicist = 'Richard Feynman'; // const physicist: "Richard Feynman"; let physicist = 'Richard Feynman'; // let physicist: string;

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

let physicist: boolean | 'Richard Feynman' | 'Schrödinger'; physicist = false; physicist = 'Schrödinger'; physicist = 100; // Error

리터럴 타입으로 선언한 변수에 원시 타입 값을 할당할 수는 없지만, 원시 타입 변수에 리터럴 타입은 할당할 수 있다.

let physicist: 'Schrödinger'; let foo = 'a'; physicist = 'Maxwell'; // Error foo = 'Maxwell';

타입 별칭

만약 타입을 여러 변수에 재사용하고 싶다면 타입 별칭을 사용하면 된다. 타입 별칭은 type 원하는 별칭 = 타입 형태로 지정한다.

type SomeType = string | number | boolean | null;

타입스크립트는 타입 별칭을 발견하면 해당 별칭이 참조하는 타입을 입력한 것처럼 작동한다. 다만, 타입스크립트의 타입 시스템에만 존재하기 때문에 런타임 코드에서는 참조할 수 없다.

type SomeType = string | number | boolean | null; let foo: SomeType; let bar: SomeType; let baz: SomeType; console.log(SomeType); // Error

또한, 타입 별칭끼리 결합하여 사용할 수도 있다.

type SomeType = string | number; type OtherType = SomeType | boolean | null;

References

https://www.typescriptlang.org/docs/handbook/

Table Of Contents
nxnaxx blog © 2022-2024 Powered By Gatsby.