Typescript 몰랐던 기능들에 대한 정리

jaejin·2023년 5월 7일

타입스크립트에서 정확하게 몰랐거나 거의 사용해보지 못한 기능들을 아래 참조 글에서 보고 내가 정리하기 위해 쓰는 글
모든 내용은 참조 글에 포함되어있으니 참조 글을 보는것을 추천
https://velog.io/@surim014/All-JavaScript-and-TypeScript-Features-of-the-last-3-years#es2022

조건부 타입

  • 어떤 타입이 다른 타입과 일치하거나 확장하는지에 따라 타입을 조건부로 설정
// 배열인 경우 배열 타입만 추출하고, 그렇지 않으면 동일한 타입을 반환합니다.
type Flatten<T> = T extends any[] ? T[number] : T;

// 배열의 요소에 대한 타입을 추출합니다.
type Str = Flatten<string[]>; // string

// 타입을 그대로 둡니다.
type Num = Flatten<number>; // number

조건부 타입으로 추론하기

  • infer 키워드를 사용해 코드에서 타입을 추론할 수 있다. infer는 임시로 추론된 타입 변수를 정의한다.
// 이전 예제를 기반으로 하여, 더 깔끔하게 작성할 수 있습니다.
type FlattenOld<T> = T extends any[] ? T[number] : T;

// 배열을 인덱싱하는 대신 배열에서 Item 타입을 추론할 수 있습니다.
type Flatten<T> = T extends (infer Item)[] ? Item : T;

// 만약 함수의 반환 타입을 가져오는 타입을 작성하고, 그렇지 않은 경우에는 undefined를 반환하도록 하고 싶다면, 이를 추론할 수도 있습니다.
type GetReturnType<Type> = Type extends (...args: any[]) => infer Return ? Return : undefined;

type Num = GetReturnType<() => number>; // number

type Str = GetReturnType<(x: string) => string>; // string

type Bools = GetReturnType<(a: boolean, b: boolean) => void>; // undefined

튜플 옵셔널 요소와 나머지

  • ?를 사용하여 옵셔널한 요소를 튜플로 선언하고 나머지는 ...를 사용하여 다른 타입에 따라 선언한다.
// 만약 튜플의 길이를 정확히는 모르지만 적어도 1 이상인 경우, `?`를 사용하여 선택적인 타입을 지정할 수 있습니다.
const list: [number, number?, boolean?] = [];
list[0] // number
list[1] // number | undefined
list[2] // boolean | undefined
list[3] // Type error: Tuple type '[number, (number | undefined)?, (boolean | undefined)?]' of length '3' has no element at index '3'.

// 기존 타입을 기반으로 튜플을 만들 수도 있습니다.
// 만약 배열의 시작 부분을 패딩하고 싶다면, rest 연산자 `...`를 사용하여 패딩할 수 있습니다.
function padStart<T extends any[]>(arr: T, pad: string): [string, ...T] {
  return [pad, ...arr];
}

const padded = padStart([1, 2], 'test'); // [string, number, number]
console.log(padded); // ["test", 1, 2] 

매핑된 타입에서의 키 리매핑

  • [K in keyof T as NewKeyType]: T[K]와 같이 값은 그대로 사용하면서 매핑된 타입의 키를 다시 지정할 수 있다.
// 객체의 형식을 다시 지정하되 해당 객체의 ID에 밑줄을 추가하고 싶다고 가정해 보겠습니다.
const obj = { value1: 0, value2: 1, value3: 3 };
const newObj: { [Property in keyof typeof obj as `_${Property}`]: number }; // { _value1: number; _value2: number; _value3: number; }

재귀적인 조건부 타입

  • 조건부 타입을 자신의 정의 내부에서 사용해서 무한히 중첩된 값을 조건부로 언팩하는 타입을 만들 수 있다.
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;

type P1 = Awaited<string>; // string
type P2 = Awaited<Promise<string>>; // string
type P3 = Awaited<Promise<Promise<string>>>; // string

분해된 변수는 명시적으로 사용하지 않는 것으로 표시 가능

  • 구조 분해할 때 밑줄을 사용하면 해당 변수를 사용되지 않는 것으로 표시할 수 있다. 타입스크립트에서 사용되지 않은 변수 오류가 발생하지 않는다.
const [_first, second] = [3, 5];
console.log(second);

// 혹은 더 짧게 사용할 수 있습니다.
const [_, value] = [3, 5];
console.log(value);

정적 인덱스 시그니처

  • 클래스에서 정적 프로퍼티를 사용할 때, static [propName: string]: string을 사용하여 인덱스 시그니처를 설정할 수 있다.
// 이전 방식
class Test {}

Test.test = ''; // Type error: Property 'test' does not exist on type 'typeof Test'.

// 새로운 방식
class NewTest {
  static [key: string]: string;
}

NewTest.test = '';

Awaited 타입 및 Promise 개선

  • 새로운 Awaited<> 유틸리티 타입은 무한하게 중첩된 Promises에서 값을 추출한다.
// 만약 제네릭한 awaited 값을 가지고 싶다면, Awaited 유틸리티 타입을 사용할 수 있습니다.
// 이를 통해 무한하게 중첩된 Promises는 모두 값을 반환합니다. (이전 예제의 소스 코드에 포함되어 있습니다.)
type P1 = Awaited<string>; // string
type P2 = Awaited<Promise<string>>; // string
type P3 = Awaited<Promise<Promise<string>>>; // string

infer 타입 변수에 대한 extends 제약 조건

  • 조건부 타입에서 타입 변수를 추론할 때 이제 extends을 사용하여 직접 범위를 좁히거나 제한할 수 있다.
// 배열의 첫 번째 요소가 string인 경우에만 가져오는 타입을 입력한다고 가정해 보겠습니다.
// 이를 위해 조건부 타입을 사용할 수 있습니다.

// infer 타입 변수에 제약 조건 확장을 사용하면 훨씬 쉽게 선언할 수 있습니다.
type FirstIfStringNew<T> =
  T extends [infer S extends string, ...unknown[]]
    ? S
    : never;

type A = FirstIfStringNew<[string, number, number]>; // string
type B = FirstIfStringNew<["hello", number, number]>; // "hello"
type C = FirstIfStringNew<["hello" | "world", boolean]>; // "hello" | "world"
type D = FirstIfStringNew<[boolean, number, string]>; // never

참조
https://velog.io/@surim014/All-JavaScript-and-TypeScript-Features-of-the-last-3-years#es2022

profile
jjlabsio

0개의 댓글