14. 타입 연산과 제너릭

ClassBinu·2024년 4월 16일

이름 붙이기

타입은 직접 지정할 수 있지만, 반복되는 타입을 이름을 지정해서 반복을 줄이는 것!(DRY)

// 타입이 반복되는 상황
function distance(a: {x: number, y: number}, b: {x: number, y: number}) {
  return;
}

// 타입에 이름 붙이기
interface Point2D {
  x: number;
  y: number;
}

function distance(a: Point2D, b: Point2D} { /*..*/ }

확장해서 반복 제거

interface Person {
  firstName: string;
  lastName: string;
}

interface PersonWithBirthDate extends Person {
  bitrh: Date;
}

인덱싱으로 반복 제거

interface State {
  userId: string;
  pageTitle: string;
  recentFiles: string[];
  pageContents: string;
}

type TopNavState = {
  userId: State['userId'];
  pageTitle: State['pageTitle'];
  recentFiles: Satete['recentFiles']
};

매핑된 타입으로 반복 제거

type TopNavState = {
  [k in 'userId' | 'pageTitle' | 'recentFiles'] : State[k]
};

매핑된 타입 추가 설명

배열의 필드를 루프 도는 것과 같은 방식이며, 표준 라이브러리에서도 자주 사용된다. Pick이라고 함.

기존 타입의 프로퍼티를 새로운 타입으로 변환하거나, 특정 프로퍼티만을 선택하여 새로운 타입을 생성

// 제네릭 타입을 이용해서 주어진 타입 T에 특정 키만 선택해서 새로운 타입 생성
// K가 T의 속성 중 하나만 될 수 있음을 명시
// 이거 선언하고 써야 됨! 그니까 이름이 꼭 Pick일 필요는 없지만 관행적으로 Pick으로 씀.
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

// 가정: State 타입이 다음과 같이 정의되어 있음
interface State {
    userId: number;
    pageTitle: string;
    recentFiles: string[];
    themeColor: string;
}

// Pick을 사용하여 State에서 특정 프로퍼티만을 추출
type TopNavState = Pick<State, 'userId' | 'pageTitle' | 'recentFiles'>;

// 결과
type TopNavState = {
    userId: number;
    pageTitle: string;
    recentFiles: string[];
}

Pick은 제너릭 타입이다. 마치 함수 호출하는 것에 비유할 수 있음.
Pick은 T와 K 두 가지 타입을 받아서 결과 타입을 반환

사용 예시

interface Options {
  width: number;
  height: number;
  color: string;
  label: string;
}

type OptionsUpdate = {
  [k in keyof Options]?: Options[k]
};

이 패턴은 표준 라이브러리에 Partial이름으로 포함되어 있음.

class UIWidget {
  constructor(init: Options) {}
  update(options: Partial<Options>) {}

제네릭이 뭐야?

제네릭(Generic)은 프로그래밍 언어에서 타입(Type)의 매개변수화를 가능하게 하는 기능

function identity<T>(arg: T): T {
    return arg;
}

let output1 = identity<string>("myString");  // output1의 타입은 'string'
let output2 = identity<number>(100);        // output2의 타입은 'number'

값으로 타입 정하기

const INIT_OPTIONS = {
  width: 640,
  height: 480,
  color: '#00FF00',
  label: 'VGA',
};

// 이렇게 안 하고
interface Options {
  width: number;
  height: number;
  color: string;
  label: string;
}
// 이렇게 하면 됨
type Options = typeof INIT_OPTIONS;

타입을 정의하고 변수에 타입을 지정하는게 아니라,
먼저 변수를 선언하고 선언된 변수에터 타입을 추출하는 방식

제너릭 타입에 익숙해지기

Partial

T라는 타입의 모든 속성을 옵셔널로 만듦

interface MyType {
  a: number;
  b: string;
}

const partial: Partial<MyType> = { a: 10 };  // b 속성은 선택적

Pick

T라는 타입에서 K로 지정된 속성만을 선택하여 새로운 타입을 만듦

interface MyType {
  a: number;
  b: string;
  c: boolean;
}

const picked: Pick<MyType, 'a' | 'c'> = { a: 10, c: true };  // b 속성은 제외

ReturnType

함수 타입 T의 반환 값의 타입을 추출하여 생성

function exampleFunction(a: number): Array<number> {
  return [a];
}

type FunctionReturnType = ReturnType<typeof exampleFunction>;  // Array<number>

0개의 댓글