내로잉은 앞서 배운 타입 추론을 어떻게 가져가는 지에 대한 논리가 들어있기 때문에 매우 중요한 개념이다!!
Union 타입에서 더욱 구체적인 타입으로 논리적으로 유추할 수 있게 되는 것을 의미한다.
간단한 예시를 보자.
let stringOrNumber: number | string = '태연';
stringOrNumber;
위의 코드에서 stringOrNumber은 어떤 타입을 가질까? string 타입
값으로 '태연'이라는 string 값을 넣어버렸기 때문이다. 이것이 바로 Narrowing의 기본이 되는 개념이다.
즉, 타입으로 선언을 했더라도 값을 통해서 해당 변수가 어떤 타입이 될 지를 추론할 수 있다. Narrowing된 타입은 그 타입으로 완전히 인지된다는 것을 기억하자!!
특정 값을 할당해서 Narrowing하는 방식, 앞서 예제가 바로 Assignment Narrowing이다.
자바스크립트의 typeof 키워드를 이용해 Narrowing하는 방식
let numOrString: number | string = Math.random() > 0.5 ? null : '태연';
if (typeof numOrstring === 'string') {
numOrstring; // type: string
} else {
numOrstring; // type: number
};
어떤 타입인지에 따른 조건을 걸어 타입을 다르게 가져갈 수 있는 코드를 작성할 때 유용할 것 같다.
false로 평가되는 값들의 타입을 체크할 때 사용하는 방식
let nullOrString: null | string = Math.random() > 0.5 ? null : '태연';
if (nullOrString) {
nullOrString; // type: string
} else {
nullOrString; // type: string | null
}
타입이 null | 배열 형태
else의 타입은 null
타입이 null | number 또는 string
else의 타입은 number | null 또는 string | null 이다.
같은 지를 비교해서 Narrowing을 하는 방식
let stringOrBoolean: string | boolean = Math.random() > '태연' ? true : '태연';
let stringOrNumber: string | number = Math.random() > 0.5 ? '태연' : 123;
if (stringOrBool2 === stringOrNumber) {
stringOrBool2; // type: string
stringOrNumber; // type: string
} else {
stringOrBool2; // type: string | true, 이 부분 제대로 이해 안됨 (타입이 string | true인 이유)
stringOrNumber; // type: string | number
}
let numberOrStringOrNull: number | string | null =
Math.random() > 0.5 ? 1123 : Math.random() > 0.5 ? '안유진' : null;
if (typeof numberOrStringOrNull === 'number') {
numberOrStringOrNull; // type: number
} else {
numberOrStringOrNull; // type: string | null
}
typeof를 쓰면 typeof 내로잉과 equality 내로잉이 동시에 작용한다는 것을 기억하자.
자바스크립트의 in 연산자를 이용한 Narrowing 방식
in연산자는 human 객체에 name이라는 프로퍼티가 있는지 확인하고 싶을 때 사용한다.console.log('name' in human); // true
interface Human {
name: string;
age: number;
}
interface Dog {
name: string;
type: string;
}
let human: Human = {
name: '태연',
age: 23,
};
let dog: Dog = {
name: '퍼피',
type: '시바견',
};
let humanOrDog: Human | Dog = Math.random() > 0.5 ? human : dog;
if ('type' in humanOrDog) {
humanOrDog; // Dog
} else {
humanOrDog; // Human
}
humanOrDog에 type 프로퍼티가 있다면 당연히 Dog 타입일 것이다. 그게 아니라면 당연히 Human 타입일 것이다.
자바스크립트의 instanceof 키워드를 이용한 Narrowing 방식
instanceof는 자바스크립트에서 클래스에 속하는지 확인하는 연산자이다.
let dateOrString: Date | string = Math.randome() > 0.5 ? new Date() : '태연';
if (dateOrString instanceof Date) {
dateOrString; // type: Date
} else {
dateOrString; // type: string
}
먼저 타입을 선언할 때 나쁜 예시부터 살펴보자.
interface Animal {
type: 'dog' | 'human';
height?: number;
breed?: string;
}
let animal: Animal = Math.random() > 0.5 ? {
type: 'human',
height: 177,
} : {
type: 'dog',
breed: '포메라니안',
};
if (animal.type === 'human') {
animal.height; // type: number | undefined
} else {
animal.breed; // type: string | undefined
animal.height; // type: number | undefined
};
다음으로 좋은 예시를 살펴보자.
interface Human2 {
type: 'human';
height: number;
};
interface Dog2 {
type: 'dog';
breed: string;
};
type HumanOrDog = Human2 | Dog2;
let humanOrDog2: HumanOrDog = Math.randome() > 0.5 ? {
type: 'human',
height: 177,
} : {
type: 'dog',
breed: '포메라니안',
};
if (humanOrDog2.type === 'human') {
humanOrDog2; // type: Human2
} else {
humanOrDog2; // type: Dog2
}
공통된 프로퍼티를 가지는 객체의 타입을 선언할 때 하나로 묶어서 선언하는 것보다 여러 개로 나눠서 선언하고, 유니언으로 묶어주는 것이 타입을 정확히 추론하는 데에 훨씬 유리하다.
checking을 하면서 narrowing을 할 수 있는 방법이다.
switch (humanOrDog2.type) {
case 'human':
humanOrDog2; // type: Human2
break;
case 'dog':
humanOrDog2; // type: Dog2
break;
default:
humandOrDog2; // type: never
const _check: never = humanOrDog2;
break;
};
default 문에서 humandOrDog2의 타입은 Human2도 아니고, Dog2도 아니기 때문에 never 타입이 된다.
다음 코드를 한 번 자세히 살펴보자.
const _check: never = humanOrDog2;
현재 humanOrDog2의 타입은 never인 상태다. 그래서 _check이라는 변수의 타입을 never로 선언해주었다. 이 코드는 추후에 오류 체크용으로 추가한 코드다.
만약에 내가 추후에 이 humanOrDog2에 Fish라는 타입을 하나 더 추가했다고 가정해보자. 그러면 switch 문에서 type의 조건을 확인할 때 default 문에서의 humanOrDog2의 타입은 never가 아닌 Fish 타입이 될 것이다. 그렇게 되면 이 코드에서 빨간 줄의 에러가 날 것이다.
지금까지 배운 다른 개념들 보다 많은 내용을 담고 있지만 하나하나 보면 개념들이 그렇게 크게 어렵지 않았다. 강의를 들으면서 배운 지식들의 연결고리가 계속 머릿 속에 이어지는 느낌을 받아서 다행인 것 같다.
string | boolean이 아니라 string | true 인 이유