타입스크립트가 타입 추론을 통해 판단할 수 있는 타입의 범주를 넘는 경우, 즉 타입추론으로는 해결할 수 없는 경우, 에러가 발생하는 것을 막기 위해 사용자가 임의로 타입을 명시할 수 있다. 이를 타입 단언이라고 한다.
아래의 예제는 isNumber 가 true 라면 val 의 타입이 number 라는 것을 의미하는걸 알 수 있다. 하지만 타입스크립트는 아직 val 의 타입이 string 또는 number 중에 선택되지 않았으므로 val.toFixed(2) 함수를 실행하는 것을 옳지 않다라고 판단하여 에러를 발생시킨다.(*isNumber 가 true 면 number 타입이라는 것은 사용자들끼리 정한 임의의 약속이므로)
function someFunc(val: string | number, isNumber: boolean) {
// some logics
if (isNumber) {
val.toFixed(2); // 에러 발생- TS2339: ... Property 'toFixed' does not exist on type 'string'.
}
}
따라서 에러가 발생하는 것을 방지하기 위해 isNumber 가 true 일때 val 이 숫자임을 타입스크립트에게 단언해주어야 한다. 이때 2가지 방식으로 단언할 수 있다.
두번째 방식(<number>val)
은 JSX 를 사용하는 경우 특정 구문 파싱에서 에러가 발생할 수 있으므로 .tsx
파일에서는 사용할 수 없다.
function someFunc(val: string | number, isNumber: boolean) {
// some logics
if (isNumber) {
// 1. 변수 as 타입
(val as number).toFixed(2);// 에러 발생 X
// Or
// 2. <타입>변수
// (<number>val).toFixed(2);
}
}
아래 예제와 같이 타입 단언을 여러 번 하게 될 경우기 존재한다.
function someFunc(val: string | number, isNumber: boolean) {
if (isNumber) {
(val as number).toFixed(2);
isNaN(val as number);
} else {
(val as string).split('');
(val as string).toUpperCase();
(val as string).length;
}
}
타입 가드를 사용하면 타입스크립트가 추론 가능한(우리가 타입가드를 통해 지정한) 특정 범위(scope) 에서 타입을 보장할 수 있다.
타입 가드가 return 하는 boolean 값이 true 라면 val is number
가 적용되어 타입 가드가 사용된 곳의 하위 코드들에게 val 의 타입이 number 로 보장된다. 반환 타입의 NAME is TYPE
형태는 타입 술부(Predicate) 라고 부른다.
// 타입 가드 ↓
function isNumber(val: string | number): val is number {
return typeof val === 'number';
}
// 사용 예제
function someFunc(val: string | number) {
if (isNumber(val)) {// 타입 가드로 val 이 number 타입인지 검사하고, 맞으면 하위 코드들에게 val 의 타입이 number 임을 보장한다.
val.toFixed(2);
isNaN(val);
} else { // number 타입이 아니므로 자동으로ing 타입이 된다.
val.split('');
val.toUpperCase();
val.length;
}
}
위 방식말고도 사용가능한 타입 가드가 더 있다. typeof
, in
, instanceof
등의 연산자를 직접 사용하는 타입 가드들이다. 따로 타입 가드를 함수로 생성할 필요없이, 연산자를 바로 사용하면 된다.
// 1. typeof
function someFuncTypeof(val: string | number) {
if (typeof val === 'number') {
val.toFixed(2);
isNaN(val);
} else {
val.split('');
val.toUpperCase();
val.length;
}
}
// 2. in
function someFuncIn(val: any) {
if ('toFixed' in val) {
val.toFixed(2);
isNaN(val);
} else if ('split' in val) {
val.split('');
val.toUpperCase();
val.length;
}
}
[참고 및 출처]
HEROPY Tech 한눈에 보는 타입스크립트