TypeScript, 문자열 패턴 매칭

다음과 같은 문자열 리터럴 타입이 있습니다.

type url = "user/profile";

위의 타입에서 "/" 문자 다음의 문자열인 "person"을 문자열 패턴 매칭을 통해 얻는 코드는 다음과 같습니다.

type Last<T> =
    T extends `${string}/${infer U}`
        ? U
        : never;

type A = Last<url>; // "profile"

TypeScript, 객체 타입의 키 값으로 구성된 Type과 키의 값으로 구성된 Type 정의

다음과 같은 객체가 있습니다.

type User = {
    name: "nameValue"
    age: "ageValue"
};

User 타입을 구성하는 키 값에 대한 타입은 "name" | "age"입니다. 이를 얻는 코드는 다음과 같습니다.

type KeysType<T extends object> = keyof T & string;
type KT = KeysType<User>; // "name" | "age"

이번에는 User 타입의 키에 대한 값에 대한 타입은 "nameValue" | "ageValue"입니다. 이를 얻는 코드는 다음과 같습니다.

type ValuesType<T extends object> = T[keyof T & string];
type VT = ValuesType<User>; // "nameValue" | "ageValue"

TypeScript, 타입으로써 {}object의 차이점

타입으로써 {}object를 구분하는 것은 타입스크립트 프로그래머로써 매우 매우 중요합니다.

먼저 {} 타입으로 정의된 변수 객체에는 nullundefined를 제외한 모든 것들을 할당할 수 있습니다. object 타입으로 정의된 변수 객체에는 원시타입 값을 제외한 모든 것들을 할당할 수 있습니다.

즉, object 타입으로 선언된 아래의 예제 코드를 참고하기 바랍니다.

let obj: object;

obj = {};
obj = [];
obj = new Date();
obj = () => {};
obj = /abc/;

obj = 10;          // 오류
obj = "hello";     // 오류
obj = true;        // 오류
obj = Symbol();    // 오류
obj = BigInt(10);  // 오류

{} 타입으로 선언된 아래의 예제 코드도 있습니다.

let x: {};

x = {};
x = [];
x = 10;
x = "hello";
x = true;
x = () => {};

x = null;          // 오류
x = undefined;     // 오류

TypeScript, {}

{}는 빈 객체입니다. 하지만 TypeScript에서는 nullundefined를 제외한 모든 값을 의미하는 특수한 타입이기도 합니다.

그래서 다음 코드가 있다고 한다면 …

type A = {}
let a: A;

다음 코드는 모두 문제가 없습니다.

a = "hello";
a = 123;
a = true;
a = [];
a = {};
a = () => {};

그러나 다음 코드는 에러입니다.

a = null;
a = undefined;

그럼 {} 타입은 어디에 쓰는 물건일까요? 바로 NonNullable 입니다. NonNullable은 다음처럼 작성되어져 있습니다.

type NonNullable<T> = T & {}

NonNullable은 타입에서 nullundefined를 제외시킨 타입을 만들어줍니다. 아래코드처럼요.

type T0 = NonNullable<string | number | undefined>;
// type T0 = string | number

type T1 = NonNullable<string[] | null | undefined>;
// type T1 = string[]

NonNullable의 구현은 TypeScript의 버전에 따라 다른데요. 좀더 오래된 4.8에서는 다음처럼 구현되어 있다는 점도 알아두시면 좋습니다.

type NonNullable<T> =
    T extends null | undefined
        ? never
        : T;

TypeScript, keyof any ?

타입스크립트로 작성된 코드를 보면 keyof any가 무엇인가… 라는 의문이 생깁니다. 결론부터 이야기하면 string | number | symbol 타입입니다. 즉, 객체를 구성하는 키가 가질 수 있는 타입입니다. 다음 코드를 보겠습니다.

type Person = {
    name: string;
    age: number;
};

type K = keyof Person;

K"name" | "age" 타입입니다. 즉, Person 객체의 key 값으로 구성된 유니언 타입입니다. 이러한 keyof의 작동을 봤을때 keyof any는 객체의 key 값으로 구성되는 타입입니다. 즉, 앞서 봤던 string | number | symbol이 됩니다. Person의 경우 키 값의 타입은 string 뿐입니다. 배열은 키 값의 타입으로 number를 갖습니다. 특수한 경우 키값의 타입으로 symbol을 가질 수도 있구요. 그래서 객체를 구성하는 키가 가질 수 있는 타입은 string | number | symbol 인 것입니다.

keyof any가 객체의 키 타입이라는 것이라는 것을 즉시 유추하기는 일반적으로 어렵습니다. 그래서 TypeScript는 keyof any와 동일한 타입으로 다음과 같은 PropertyKey 타입을 미리 정의두고 있습니다.

type PropertyKey = string | number | symbol;