동적 데이터를 처리하는 다양한 방법.

동적 데이터를 처리해야 하는 상황

npm jwt 라이브러리와 typescript를 함께 사용할 때 어느 시점에서 유형으로 무엇을 해야할지 생각해야 합니다.

jwt는 내가 원하는 키:밸류 를 이용하여 서명을하고 검증을 할 수 있다.

token = jwt.sign({id: "1234"}, secret)
을 통해 얻은 token을 쿠키에 담아 클라이언트로 응답한다.

클라이언트는 서버에 요청할 때마다 쿠키를 같이 보내게 되고, 서버는 쿠키에서 토큰을 꺼내 검증한다.
result = jwt.verify(token, secret);

이 때, verify로 얻은 result에 id가 있는지 아닌지는 verfiy의 리턴 타입만으론 알 수 없다.
verify의 리턴은 다음과 같다.

export interface JwtPayload {
    (key: string): any;
    iss?: string | undefined;
    sub?: string | undefined;
    aud?: string | string() | undefined;
    exp?: number | undefined;
    nbf?: number | undefined;
    iat?: number | undefined;
    jti?: string | undefined;
}

위의 코드를 보면 jwt가 JwtPayload 유형으로 서명된 상태에서 주입된 ID를 가져올 방법이 없습니다. 이걸 해결하려면 어떻게 해야 할까요…

방법 1 유형 어설션(캐스팅)

1. 타입 단언(타입 캐스팅)
interface IPayload extends JwtPayload {
	email: string;
}

result = jwt.verify(token, secret) as IPayload;

// 이후엔 result.email로 접근하여 사용할 수 있다.
// 하지만, 타입 캐스팅이기에 result에 정말 email 프로퍼티가 있는지는 모른다
// 다음 예제 코드를 보면, result.email이 없는게 확실하지만, 타입 단언은 이를 무시한다.
interface A {
    id: string;
}

interface B extends A {
    email: string;
}

function GetA(): A {
    return {id: "MyId"};
}

const result = GetA() as B;
console.log(result.email); // undefined

연산자의 방법 2

in 연산자는 주어진 키가 개체에 있는지 여부를 테스트하는 연산자입니다. 유형 제약 조건은 in 연산자와 잘 작동합니다.

interface A {
    id: string;
}

interface B extends A {
    email: string;
}

function GetA(): A {
    return {id: "MyId"};
}

const result = GetA();
if('email' in result) {
    console.log(result.email);
}

방법 3 유형 술어

interface IPayload extends Payload {
    id: string;
}

function hasId(obj: any): obj is IPayload {
    return (obj.id !== undefined);
}

const decoded = jwt.verify(token, process.env.JWT_SECRET!);
if(hasId(decoded)){
	// 타입이 좁혀져서 decoded.id 와같이 접근 가능하다
    decoded.id  = ...
}

방법 4 어설션 서명

이것은 TypeScript 3.7에 도입된 새로운 기능이며 다음과 같이 사용할 수 있습니다.

function assert(condition: any, msg?: string): asserts condition {
	if (!condition) {
    	throw new AssertionError(msg);
    }
}

function yell(str) {
	assert(typeof str === "string");
    // assert 이후로 str은 string으로 확실하게 좁혀진다
    
    //아래 함수는 철자가 잘못되었으므로 타입스크립트 컴파일러가 에러를 잡아준다.
    return str.toUppercase();
}

졸업 증서

방법이 너무 많습니다. 이 모든 방법을 외우고 암기하는 것은 좋은 방향이 아닌 것 같습니다. 중요한 것은 원칙을 이해하고 적용하는 것입니다. 유형을 한정할 수 있는 구문(유형 보호)을 발견하면 TypeScript 컴파일러가 이를 분석한 다음 코드에서 유형의 범위를 좁힙니다(유형 내향).