본문 바로가기

# Tech/NodeTS

타입스크립트 런타임 타입 가드



타입 체크는 컴파일에 이루어진다.


타입스크립트의 타입 체크는 Only 컴파일 시간에 이루어집니다.


컴파일 시간에는 비교적 엄격하게 타입체크를 하는편이지만,

그 외에는 타입체크가 거의 없다시피 하기때문에,

런타임시에는 진짜 그 타입이 맞는지 체크하는 것이 어렵습니다.


이 와중에 any가 포함되면 믿었던 컴파일 타입체크도 무용지물이 되는데,

아래 코드가 오류없이 컴파일되는것만 봐도 알 수 있습니다.

interface Args {
    n: number;
    s: string;
}

let invaildArgs: any = {
    num: 3,
    str: "str"
};
let fakeArgs: Args = invaildArgs;

console.log(fakeArgs.n, fakeArgs.s);

위의 코드에서 fakeArgs는 실제로는 Args 인터페이스를 구현하지 않았기 때문에,

console.log에서 undefined를 출력할 것입니다.


이러한 사건, 사고가 발생하는 것을 막기위해,

진짜 이 타입이 맞아?라고 확인하는 작업을 타입 가드type guard라고 합니다.





User Define Type Guard


타입스크립트의 공식 문서에는 

사용자 정의 타입 가드User Define Type Guard를 만들어서 사용할 것을 권고하고 있습니다.


용어만 길지 개념은 어렵지 않은데,

간단히 말하자면 필드를 하나씩 체크하는 것입니다.

function ArgsGuard(target: Args): boolean {
    if (target == undefined) return false;
    if (target.n == undefined) return false;
    if (target.s == undefined) return false;
    if (new Object(target.n).constructor != Number) return false;
    if (new Object(target.s).constructor != String) return false;
    return true;
}

이러한 타입 가드는 매우 효과적이지만, 비효율적입니다.

하나하나 타입가드가 필요하기 때문이죠.


물론 ts-auto-gaurd를 사용하면 자동으로 타입가드를 작성해주므로

불편을 덜 수 있긴 합니다만, 여기서는 다른 방법을 소개할 예정입니다.





class-validator


class validator는 클래스의 유효성을 검사하는 패키지이며,

데코레이터 기능을 사용하여 필드를 정의합니다.


먼저 tsconfig에서 아래 옵션을 다음과 같이 바꿔주세요.

  • target : "es6" 또는 그 이상의 버전
  • experimentalDecorators : true
  • emitDecoratorMetadata : true


기본적인 예제는 다음과 같습니다.

import {
    IsNumber,
    validateSync,
    IsString,
    ValidationError
} from "class-validator";

class Args {
    @IsNumber()
    n!: number;

    @IsString()
    s!: string;
}
let invaildArgs: any = {
    num: 3,
    str: "str"
};
let fakeArgs: Args = Object.assign(new Args(), invaildArgs);
let errList: ValidationError[] = validateSync(fakeArgs);

console.log(JSON.stringify(errList, null, 4));
// errList[0] : "n must be a number conforming to the specified constraints"
// errList[1] : "s must be a string"



재귀적인 유효성 검사도 가능합니다.

import {ValidateNested} from "class-validator";

export class Post {
    @ValidateNested()
    user: User;

}


여기서는 타입가드 용도로만 사용했지만,

이메일 형식, 배열 길이제약 등 여러가지 유효성 검사도 넣을 수 있습니다.