티스토리 뷰

 

라이브러리 이름도 Typescript 인 것처럼 매우 유연하게 타입 추론을 가능하게 하기 위해서 타입스크립트에서는 개발자들에게 여러가지 유용한 기능들을 제공합니다. (해당 글에서는 주제와 같이 조건부 타입을 위주로 작성되었습니다.)

 

해당 글에서는 몰라도 딱히 상관은 없지만 알면 한 평생 유용한 써먹을 수 있는 타입스크립트의 기능들을 소개하겠습니다

 

keyof (유니온 타입으로 변환)

해당 키워드는 객체 및 인터페이스 또는 함수 시그니처, 멤버 변수 이름 등을(여기에서 키는 주로 어떠한 값에 접근하거나 식별하는데 사용되는 이름을 의미합니다.) 유니온 타입(Union Type)으로 변환 및 추출하는데 사용됩니다, 해당 기능은 앞서 설명했다시피 잘만 사용한다면 매우 유용하게 사용될 수 있는 기능입니다.

 

interface User {
    name: string,
    age: number
}

type UserKeys = keyof User; // "name" | "age"

해당 코드를 보면 User 인터페이스의 키들을 유니온 타입 형태로 반환해주는 것을 볼 수 있습니다, 참고로 유니온 타입은 주로 자동 완성 기능과 조건부 타입에 사용됩니다.

class User {
    iq: number = 60; // 플레이어의 지능 지수
    kill() {
        console.log("으앙 나 죽엇떵");
    }
}

type UserKeys = keyof User; // "iq" | "kill"

또한 해당 코드를 보면 객체의 경우에도, 마찬가지로 멤버 변수의 이름 또는 함수 이름들을 모두 유니온 타입으로 변환해주는 것을 볼 수 있습니다.

class User {
    iq: number = 60; // 플레이어의 지능 지수
    author: {
    	name: string,
        age: number
    }
}

type UserKeys = keyof User["author"]; // "name" | "age"

참고로 해당 코드에서 다음과 같이 특정 멤버 변수의 타입을 참조하여 이를 유니온 타입으로 변환할 수도 있습니다. (속성 값의 타입을 참고하는 경우)

enum UserStatus {
    none,
    forward,
    backward
}

type UserStatusValues = keyof typeof UserStatus;

열거형 타입의 경우도 마찬가지로 다음과 같은 방식으로 값의 식별자 이름들을 유니온 타입으로 변환 할 수 있습니다, 참고로 typeof를 거치지 않고 값을 추출하려고 한다면 "toString"과 같은 함수 식별자 이름들을 반환하기 때문에 이를 인지하고 코드를 작성하시길 바랍니다. (타입스크립트에서 typof를 명시하지 않고 enum 값을 참조한다면 이는 기본적으로 타입이 아닌 인스턴스를 가리키기 때문에)

 

in keyof (유니온 타입 변환 + 순회 및 할당)

해당 키워드는 유니온 타입으로 변환하고 이를 순회하여 변수에 할당합니다, 주로 조건부 타입을 구성하는데 사용됩니다. (참고로 [P in K]: T[P]와 같이 in 키워드만을 사용하여 순회하고 이를 변수에 할당할 수 있습니다.)

 

type Partial<T> = {
    [P in keyof T]?: T[P];
};

해당 코드는 기본적으로 타입스크립트에서 제공되는 간단한 유틸리티 타입들을 구성하는 코드의 일부입니다, Partial는 모든 속성 타입을 Nullable으로 변환하는 기능을 수행합니다.

 

해당 코드를 자세히 살펴보면 주어진 T 타입을 모두 유니온 타입으로 변환하고 이를 순회하며 P에 정의합니다, 따라서 P는 속성의 식별자 이름이 되고 값의 타입은 주어진 T 값의 P의 타입이 되는 것을 알 수 있습니다.

type Required<T> = {
    [P in keyof T]-?: T[P];
};
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

해당 코드에서도 마찬가지로 in keyof를 사용하여 타입을 재구성하는 것을 볼 수 있습니다.

 

extends keyof (유니온 타입 변환 + 조건부 타입)

해당 키워드는 유니온 타입으로 변환한 다음, 그 중 조건에 해당하는 것만 포함하도록 제한합니다, 해당 키워드는 상대적으로 매우 쉬울 수 있는게 이미 조건부 타입 (Conditional Type)의 존재를 아는 분들은 금방 이해할 것이기 때문입니다.

 

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

해당 코드를 자세히 보면 K extends keyof T의 경우 주어진 T 타입을 유니온 타입으로 변환함으로서 K는 T의 식별자 이름들로 값의 범위가 제한되는 것을 알 수 있습니다.

interface User {
    name: string,
    age: number
}

// Type '"a"' does not satisfy the constraint 'keyof User'. (2344)
const user: Pick<User, "a">;

따라서 다음과 같이 User 인터페이스 속성에서 a가 존재하지 않음으로 K의 제약 조건을 만족하지 못하여 예외가 발생하게 됩니다.

 

infer (타입 추론)

해당 키워드는 마찬가지로 조건부 타입에서 사용되는데요, 이 키워드는 조건에 따라 자동으로 타입을 추론해주는 기능을 수행합니다, 당연하게도 조건부 타입에서만 사용되는 녀석이므로 extends 절에서만 사용할 수 있습니다.

 

type ArrayType<T> = T extends (infer U)[] ? U : T;

먼저 간단한 예시를 살펴보면, 해당 코드는 T가 배열이라면 infer U는 배열 값의 타입이 되며, 따라서 주어진 T 배열의 타입을 반환합니다.

 

보통 이런 형식적인 말은 한번에 이해할 수 없는 경우가 많기 때문에 해당 예시를 확장하는 추가적인 예시를 하나를 더 살펴보도록 하겠습니다.

const array = [1, 2, 3];

function test(value: ArrayType<typeof array>) {
    // 인자 value의 타입은 추론된 number[]의 값 타입, 즉 number이(가) 됨.
}

해당 경우 array의 추론된 타입은 number[], 즉 정수 배열이므로 앞서 언급된 ArrayType은 배열 값 타입을 반환하므로 number가 됩니다.

type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

마지막으로 타입스크립트에서 기본적으로 제공되는 코드를 보면서, 해당 조건부 타입이 무엇인지 간단하게 살펴보고 마치도록 하겠습니다.

 

주어진 T가 함수이라면 (...args: any) => infer R, 즉 반환되는 값의 타입을 추론하여 반환해주는 것을 알 수 있습니다, 또한 만약 추론하지 못했다면 any 타입을 반환한다는 사실도 쉽게 알 수 있습니다.

 

Happy coding in the another world!