Typescript 어려운 부분

Diff

type Diff<T, U> = T extends U ? never : T;
type DiffExample = Diff<'a'|'b'|'c'|'d', 'a'|'c'|'f'>; // DiffExample type에는 'b' 혹은 'd'만 들어갈 수 있다.

const one : DiffExample = 'b';
const two : DiffExample = 'd';
const three : DiffExample = 'c'; // Error

Partial

interface IPerson
{
  name: string;
  age: number;
  gender: string;
}

// age가 없기 때문에 에러를 뿜는다
const person : IPerson = {
  name: "윤병인",
  gender: "남자"
}

/**
* 인터페이스의 모든 프로퍼티를 optional하게 변경한다. 그래서 name, age 가 없어도 에러를 내지 않는다.
*/
type PartialPerson = Partial<IPerson>;
const partialPerson: PartialPerson = {
  gender: "male" // optional
}

DeepPartial

type DeepPartial<T> = { [K in keyof T]?: DeepPartial<T[K]> }
interface IPerson
{
  name: string;
  age: number;
  gender: string;
  hobby: {
    alone: string,
    together: string
  }
}
const person : IPerson = {
  name: "윤병인",
  age: 25,
  gender: "남자",
  hobby: { // alone이 없기 때문에 에러가 난다.
    together: "축구"
  }
}
// hobby안에 있는 모든 요소까지(Deep~) Partial로 만들어 버리기 때문에 에러가 안난다.
let deepPartialPerson: DeepPartial<IPerson> = {
  name: "윤병인",
  age: 100,
  hobby: {
    alone: "nice"
  }
}

DeepPartial은 단순히 Partial을 제귀적으로 사용해서 모든 요소를 optional로 만든것일 뿐이다. Typescript에 기본 내장되어 있지는 않고, Redux의 index.d.ts파일에 있어서 적어봤다.

Required

/**
* 인터페이스의 모든 프로퍼티를 required하게 변경한다. 그래서 name, age, gender모두 다 입력해 줘야한다.
*/
type RequiredPerson = Required<PartialPerson>;
const requiredPerson: RequiredPerson = {
  name: "Jade", // required
  age: 29, // required
  gender: "male" // required
}

Pick

/**
* 인터페이스의 프로퍼티중 일부(name, age)만 받도록 설정한다.
*/
type PickPerson = Pick<IPerson, "name" | "age">;
const pickPerson: PickPerson = {
  name: "Jade", // required
  age: 29, // required
  // gender: "male" --> (X)
}

Exclude

Diff와 같은 기능을 한다. 하지만 Exclude는 Typescript 2.8버전부터 Built-in으로 지원된다.

type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "b" | "d"
const test1 : T00 = "b";
const test2 : T00 = "d";
const test3 : T00 = "c"; // c는 들어올 수 있다.

Omit

Typescript에서 기본적으로 제공하는 기능은 아니지만 Pick과 Exclude를 활용해서 만들 수 있다. 어떤 타입의 특정 속성을 떨궈내는데 사용한다.

type Omit<T, U extends keyof T> = Pick<T, Exclude<keyof T, U>>

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

type Animal = Omit<Person, "phoneNumber">

const test: Animal = {
  name: "Kity",
  age: 6,
  gender: "girl"
}

Interface의 속성을 떨궈내는건 안되고, type만 되는것 같다.

그런데 어떻게, “phoneNumber”가 U extends keyof T의 U로써 들어갈 수 있는걸까? U extends keyof T의 정확한 의미는 뭘까?

keyof T

keyof T는 인덱스 타입 쿼리 연산자다. 어떤 타입의 T에 대해서, keyof T는 T의 알려진 공개 프로퍼티 이름들의 합집합입니다.

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

const personProperty01: keyof Person = "name";
const personProperty02: keyof Person = "age";
const personProperty03: keyof Person = "gender";
const personProperty04: keyof Person = "phoneNumber";

U extends keyof T

이건,,,왜 extends를 쓰는지 모르겠는데, 동작하는 모습으로 봐서는 T의 key들중 하나를 넣는데 그게 U가 되는것 같다. 저게 extends가 아니라 in이었으면 좀 이해가 쉬웠을것같은데,,, 깊은 의미는 잘 모르겠다.

Conditional Type

type TypeName<T> =
  T extends string ? "string":
  T extends number ? "number":
  T extends boolean ? "boolean" : "object"

type T0 = TypeName<string>; // T0 = string
type T1 = TypeName<number>; // T1 = number

infer

참조

  1. https://ithub.tistory.com/239
  2. https://adhrinae.github.io/posts/helper-types-in-typescript
  3. https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html
  4. https://stackoverflow.com/questions/53099089/difference-between-of-k-extends-keyof-t-vs-directly-using-keyof-t

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다

Up Next:

첫 간단 외주 후기

첫 간단 외주 후기