티스토리 뷰
[ Narrowing ]
타입스크립트에서 함수 내부 또는 순차적으로 진행되는 로직 안에서 유니언 타입으로 여러 멤버 타입을 받게 되면 프로그램에서 해당 값을 처리할 때 오류가 나지 않게 Narrowing(좁히기) 처리를 해주어야합니다.
해당 내용은 타입스크립트 문서를 통해서 공부를 진행 했으며 다음 링크를 참고하시면 됩니다.
레퍼런스: https://www.typescriptlang.org/docs/handbook/2/narrowing.html
유니언 타입에서의 대처
다음과 같이 매개변수에서 유니언 타입을 받는 함수가 있습니다. 이 함수의 parseInt의 인자로 num 값을 넘겨주면 오류가 납니다. parseInt의 첫번째 인자는 string 타입만을 받기 때문입니다.
function stringNumParse(num: string | number):number {
return parseInt(num, 10);
// Argument of type 'string | number' is not assignable to parameter of type
// 'string'. Type 'number' is not assignable to type 'string'.
}
이때 typeof 를 통해 해당 코드가 실행되는 범위를 좁혀주어 해결할 수 있습니다.
function stringNumParse(num: string | number): number {
if (typeof num === "string") {
return parseInt(num, 10);
// num type : string
} else {
return num;
// num type : number
}
}
if 문과 typeof 로 이렇게 코드의 범위를 좁혀주는 형태는 자바스크립트의 코드를 짤 때 많이 보던 패턴입니다. 타입스크립트는 이러한 자바스크립트의 코드 스타일 방식을 최대한 침해하지 않으며 타입을 제공하고 있습니다. 구체적으로 타입의 유형을 축소시키는 검사를 타입 가드(type gaurd)라고 합니다.
[ typeof ]
자바스크립트의 연산자 typeof 를 통해서 타입 가드가 가능합니다. 해당 값에 (typeof {값} === 타입) 의 분기를 통해서 각 코드의 범위를 좁힐 수 있습니다.
자바스크립트의 typeof를 통해서 타입 가드를 할 수 있는 유형들
- "string" , "number" , "bigint" , "boolean" , "symbol" , "undefined" , "object" , "function"
function typeGuard(
value:
| string
| number
| bigint
| boolean
| symbol
| undefined
| object
| Function
) {
if (typeof value === "string") {
// ...
} else if (typeof value === "number") {
// ...
} else if (typeof value === "bigint") {
// ...
} else if (typeof value === "boolean") {
// ...
} else if (typeof value === "symbol") {
// ...
} else if (typeof value === "undefined") {
// ...
} else if (typeof value === "object") {
// ...
} else if (typeof value === "function") {
// ...
}
}
[ 진실성 축소 (Truthiness narrowing) ]
자바스크립트의 조건부인 &&, ||, ! 등의 표현식을 사용해서 타입 가드를 해줄 수 있습니다.
function numberOfPeople(number: number) {
if (number) {
// number 가 0 이 아닐 때
// 자바스크립트에서 숫자 0은 if 문에서 false와 같이 동작하고 나머지는 true와 같이 동작합니다.
// ...
} else {
// number 가 0 일 때 동작
// ...
}
}
if 조건문에서 false와 같이 동작하는 값들
- 0
- NaN
- " " (빈 문자열)
- 0n (bigint 제로 버전)
- null
- undefined
boolean 과 같이 동작하기
강제로 타입이 다른 값을 boolean 과 같이 동작하게 하는 방법이 있습니다.
자바스크립트의 Boolean 이나 !! 연산자를 사용하면 됩니다.
function numberOfPeople(number: number) {
if (Boolean(number)) {
//...
} else {
//...
}
}
// or
function numberOfPeople(number: number) {
if (!!number) {
//...
} else {
//...
}
}
진실성 축소를 통해서 값의 존재 여부 판단 후 로직 실행
function debug(strs: string | string[]) {
// && 연산자와 함께 진실성을 체크하여 코드 범위를 축소합니다.
if (strs && typeof strs === "object") {
for (const st of strs) {
console.log(st);
}
}
// ...
}
등식 축소(Equality narrowing)
타입스크립트는 자바스크립트의 동등성 검사를 할 수 있는 ===, !==, ==, != 와 같은 연산자를 통하여 타입 가드를 할 수 있습니다.
아래에서는 === 을 통해서 두 값이 같은지 판별하여 두 값이 같을 땐 string 타입일 때 밖에 없으므로 코드 범위를 축소 시킬 수 있습니다.
function debug(
numOrString: string | number,
stringOrBoolean: string | boolean
) {
if (numOrString === stringOrBoolean) {
numOrString.toUpperCase();
// type : (method) String.toUpperCase(): string
stringOrBoolean.toUpperCase();
// type : (method) String.toUpperCase(): string
} else {
console.log(numOrString);
// type : (parameter) numOrString: string | number
console.log(stringOrBoolean);
// type : (parameter) stringOrBoolean: string | boolean
}
}
동등성 연산자를 통해서 다음과 같이 null 이 들어올 수 있는 상황에서 대처를 할 수 있습니다.
function debug(strs: string | string[] | null) {
// strs 가 null이 아닐때만 코드 로직이 실행 될 수 있도록 하였습니다.
if (strs !== null) {
/*
if(typeof strs === "???"){
//...
}
*/
}
}
in 연산자 축소( in operator narrowing)
자바스크립트의 객체에 프로퍼티가 있는지 판별하는 in 연산자를 통해서 타입 축소도 가능합니다.
type Fish = { swim: () => {} };
type Bird = { fly: () => {} };
function action(animals: Fish | Bird) {
if ("fly" in animals) {
return animals.fly();
} else {
return animals.swim();
}
}
instanceof 축소
해당 값이 다른 값의 인스턴스인지 확인하는 자바스크립트의 instanceof 를 통해서 타입 가드로 코드 범위를 좁힐 수 있습니다.
interface Car {
name: string;
age: number;
move: () => void;
}
class SpecialCar {
constructor() {
// ...
}
fly() {}
}
function carCheck(cars: Car | SpecialCar) {
if (cars instanceof SpecialCar) {
// ....
cars.fly();
// (parameter) cars: SpecialCar
} else {
// ...
cars.move();
// (parameter) cars: Car
}
}
타입 명제 (type predicates)
타입이 어떤 타입을 반환하는지 체크하는 함수를 타입스크립트를 통해서 만들 수 있습니다.
다음과 같이 일반 유저와 특별한 유저를 반환하는 함수가 있습니다.
interface User {
name: string;
}
interface SpecialUser extends User {
specialKey: number;
}
function getUser(): User | SpecialUser {
return { name: "Tom", specialKey: 10 };
}
let user = getUser();
반환된 user 변수를 반환된 형식에 맞춰 로직을 실행 할 때 함수를 만들어 주면 되지만 명시적으로 타입스크립트에서 어떤 값이 반환될 것인지를 지정해 줄 수 있습니다. is 를 통해서 반환될 값이 어떤 값일지 명시적으로 표시해줍니다.
function isSpecialUser(user: User | SpecialUser): user is SpecialUser {
return (user as SpecialUser).specialKey !== undefined;
}
// ...
let user = getUser();
if (isSpecialUser(user)) {
console.log(user.specialKey);
// type : SpecialUser
} else {
console.log(user.name);
// type : User
}
구별된 유니온( Discriminated unions )
유니온 타입을 하나의 프로퍼티에서 사용하지 않고 구별된 인터페이스를 통해서 사용합니다.
//...
interface SpecialUser extends UserInfo {
type: "special";
specialKey: number;
}
interface NormalUser extends UserInfo {
type: "normal";
}
type User = SpecialUser | NormalUser;
이를 바탕으로 타입을 좁혀주어 코드를 처리해줍니다.
function userCheck(user: User) {
if (user.type === "special") {
//... special 유저인 경우 처리
} else {
//... 나머지 유저 인경우 처리
}
}
그렇다면 나머지 유저인 경우에서도 처리를 명시적으로 하고 하지 않았을 때에 오류를 내보내주고 싶을땐 어떻게 할까요?
function userCheck(user: User) {
if (user.type === "special") {
//... special 유저인 경우 처리
} else {
//... 나머지 유저 인경우 처리
}
// normal 유저도 처리를 명시적으로 하고 싶습니다.
}
철저한 검사 (Exhaustiveness checking)
다음과 같이 never을 사용해서 나머지에서도 완전한 타입 체크를 하는 상황을 발생시키면 예외인 처리를 해줄 수 있습니다.
type User = SpecialUser | StoreUser | NormalUser;
function userCheck(user: User) {
if (user.type === "special") {
//... special 유저인 경우 처리
} else if (user.type === "store") {
//... store 유저인 경우 처리
} else if (user.type === "normal") {
// ... normal 유저인 경우 처리
} else {
const _exhaustiveCheck: never = user;
}
}
switch 문으로도 동작합니다.
function userCheck(user: User) {
switch (user.type) {
case "special":
// ...
return user.specialKey;
case "store":
// ...
return user.storeName;
case "normal":
//
return user.name;
default:
const _exhaustiveCheck: never = user;
return _exhaustiveCheck;
}
}
'Typescript' 카테고리의 다른 글
[ Typescript ] Object 타입스크립트에서의 객체 타입 (0) | 2022.09.09 |
---|---|
[ Typescript ] function 타입스크립트에서의 함수 (0) | 2022.09.07 |
[Typescript] 타입 스크립트의 기본 (0) | 2022.09.03 |
[Typescript] 타입스크립트 핸드북 시작하기 (0) | 2022.09.03 |