Narrowing
TS에서 타입 검사를 통해 조건을 좁혀서 특정 행동을 하는 것을 통칭하여 Narrowing이라고 한다.
Type Assertions
타입 단언은 말 그대로 타입을 단언적으로 명시해주는 것이다. 타입 스크립트가 타입 추론을 통해 판단할 수 있는 타입의 범주를 넘어서는 경우, 더이상 추론하지 않도록 직접 지시한다.
💡타입 단언은 C나 JAVA의 타입 형변환과는 다른 개념이다. 형변환은 실제로 데이터 자료를 변환시키지만, 타입 단언은 실제 데이터를 바꾸는 것이 아니라 타입 정보를 명확히 남기는 것이므로 오직 컴파일 과정의 타입체킹에만 사용된다. (코드 자체에 에러는 발생하지 않지만 실행하다보면 오류를 일으키기도 한다.)
타입 단언 선언 방법
document.getElementById를 id가 main_canvas인 요소에 적용하면 HTMLElement 여러 종류 중 한 가지를 반환한다. 만약, HTMLCanvasElement라는 특정 요소를 반환하도록 지정하고 싶다면 타입 단언을 통해 세팅할 수 있다.
1. as
const myCanvas = document.getElemetById("main_canvas") as HTMLCanvasElemet;
2. angle-bracket
const myCanvas = <HTMLCanvasElemet>document.getElementById("main_canvas");
- <> 방법은 태그 엘리먼트와 문자가 겹칠 수 있으며, 제네릭과 헷갈릴 수 있으니 웬만하면 as로 표현하는 것이 추천된다.
- 타입 단언은 해당 변수가 unknown 타입일 경우에만 사용하는 걸 추천된다. 나머지 상황일 때는 타입 가드를 이용해 해결하는 것이 좋다. 타입 단언은 개발자가 해당 타입에 확신이 있다고 사용했기에, 컴파일러는 별도로 타입 체크를 하지 않고 데이터의 구조도 신경 쓰지 않게 되므로 오류를 발생할 확률이 높다.
- 타입 단언 | 타입스크립트 핸드북
Type Guards
타입 가드는 에러를 줄일 수 있는 방어 코드 기법을 말한다.
function padLeft(padding: number | string, input: string ) : string {
return new Array(padding + 1).join(" ") + input;
}
해당 코드는 padding이 number일수도 string일수도 있기 때문에 a가 number일 경우 에러가 날 것을 TS가 캐치해서 경고를 해준다.
이럴 때는 아래와 같이 typeof 연산자 이용해 조건 분기를 잘 해주면 된다.
function padLeft(padding: number | string, input: string ) : string {
// typeof 없이 return을 하면 padding이 number일 가능성을 TS가 체크하여, ERROR
if (typeof padding === "number") {
return new Array(padding + 1).join(" ") + input;
}
return padding + input;
}
[대표적인 타입 가드 operator]
- typeof: 일반 타입 체킹
- instanceof: 클래스 체킹
- Array.isArray(): 배열 체킹
- .type/in: 객체 속성 체킹
📘 타입 추론 / 타입 호환 / 타입 단언 / 타입 가드 💯 총정리
이러한 키워드는 모두 JS 코드이다. 하지만 JS에서는 타입 선언이 유연하기 때문에 이 연산자들을 쓸 일이 별로 없다. 하지만 TS는 타입 체킹을 중요시하므로 TS에서 많이 쓰인다.
Truthiness Narrowing
TS에서 &&, ||, if와 같은 조건문, !와 같은 연산자를 통해 narrowing을 할 수 있다.
위에서 살펴보았던 padLeft 함수 인자인 padding에 | null을 추가하여 null이 넘어올 수 있다고 가정해보자.
그럼, null을 narrowing하기 위해 if(padding)을 추가해줄 수 있다. 하지만 이렇게 널 체크를 하면 빈 문자열 (’ ‘) 또한 false로 반환하여 원하는 대로 동작하지 않는다.
[if (padding !== null)로 해결]
function padLeft(padding: number | string | null, input: string) : string {
if (padding !== null) {
if (typeof padding === 'number') {
return new Array(padding + 1).join(' ') + input;
}
return padding + input;
}
return input;
}
Equality Narrowing
function func1 (x: string | number, y: string | boolean) {
if (x === y) {
x.toUpperCase();
y.toUpperCase();
} else {
console.log(x, y);
}
}
x와 y의 값과 타입이 같은 경우를 if 분기로 체크한 다음, 연산을 수행할 수 있도록 Equality Narrowing을 한 예제이다.
in Operator Narrowing
if (’swim’ in animal) 을 사용함으로써 animal에 swim이라는 프로퍼티가 존재하는지 확인하고, 존재한다면 animal의 union 중 어떤 값인지에 대한 사실을 알게되는 것이다. 그래서 if 문 밖의 animal은 Cat임을 알고, 위 사진처럼 jump 프로퍼티에 대한 자동완성을 해준다.
instanceofNarrowing
function logVal(x: Date | string) {
if (x instanceof Date) {
// x의 parameter : Date
console.log(x.toUTCString());
}
else {
// x의 parameter : string
console.log(x.toUpperCate());
}
}
instanceof는 x가 다른 값의 인스턴스인지 아닌지 확인하는 연산자이다. 예제를 통해 이 연산자로 값의 타입을 체킹하는 것을 확인할 수 있다.
Discriminated Unions
// ? optional으로 circle이면 radis를, square면 sideLength를 사용.
interface Shape {
kind: 'circle' | 'square';
radius?: number;
sideLength?: number;
}
function getArea (shape: Shape){
if (shape.kind === 'circle') {
return Math.PI * shape.radius ** 2;
}
}
Shape 인터페이스에서 redius는 옵셔널하게 선언되었다. 이 뜻은 해당 속성이 없을 수도 있다는 뜻이다.
따라서 TS 컴파일러는 shape.radius가 undefind일 수 있다는 가능성을 경고한다.
interface Circle {
kind: 'circle';
radius: number;
}
interface Square {
kind: 'square';
sideLength: number;
}
type Shape = Circle | Square;
function getArea (shape: Shape){
switch(shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square' :
return shape.sideLength ** 2;
}
}
이와 같이 인터페이스를 분리하고 switch-case 문으로 코드를 훨씬 더 간결명료하게 구현할 수 있다.
결론적으로 TS는 개발자가 선언한 변수, 프로퍼티, 인수들에 대한 타입을 엄격히 관리해주고 실수를 방지하는 여러 장치를 제공한다는 것을 알 수 있었다. 👍
'언어 > JS & TS' 카테고리의 다른 글
[TypeScript] 함수 표현식 & Call∙Constructor Signature (1) | 2023.11.29 |
---|---|
[JavaScript] JS의 논리연산자, OR과 AND (0) | 2023.11.28 |
[JavaScript] JS의 this (0) | 2023.11.27 |
[TypeScript] TS 기본 지식 | 정적타입언어, tsc, 타입 (0) | 2023.11.21 |
[넘블챌린지] week 1-1. 주요 기능 파악과 구현 목록, 목표 세우기 (0) | 2023.02.26 |