StudyForYou / ouahhan-typescript-with-react

우아한 타입스크립트 with 리액트 스터디 레포 🧵
4 stars 0 forks source link

#17 [5장_3] 하나의 유틸리티 타입을 소개하고 특징과 예시를 들어서 설명해주세요. #29

Closed hyeyoonS closed 2 months ago

hyeyoonS commented 2 months ago

❓문제

중복된 타입보다는 다른 팀원이 올린 설명에서 보충 설명 혹은 새로운 타입에 대한 타입 설명이면 좋겠네요 🙂

🎯답변

기본 유틸리티 타입과 커스텀 유틸리티 타입에 대해 설명하겠습니다.

1. Pick<T, K>

특징: 특정 객체 타입으로부터 특정 프로퍼티만을 골라내는 타입입니다.

interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

const legacyPost: Pick<Post, "title" | "content"> = {
  title: "",
  content: "",
};
// 추출된 타입: { title: string; content: string }

구현 방법

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

2. Omit<T, K>

특징: 특정 객체 타입으로부터 특정 프로퍼티만을 제거하는 타입입니다.

interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

const noTitlePost: Omit<Post, "title"> = {
  content: "",
  tags: [],
  thumbnailURL: "",
};

구현 방법

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

[커스텀 유틸리티 타입]

1. Primitive

특징: 자바스크립트의 원시 데이터 타입만 모아둔 타입입니다.

type Primitive = string | number | bigint | boolean | symbol | null | undefined;

2. Falsy

특징: 자바스크립트의 falsy한 모든 값을 모아둔 타입입니다.

type Falsy = false | "" | 0 | 0n | null | undefined;

3. Truthy

특징: falsy 값을 걸러내고 truthy 값만 추출하는 타입입니다.

type Example = Truthy<"" | 1 | false | {} | undefined>;
// Result: 1 | {}

구현 방법

type Truthy<T> = T extends Falsy ? never : T;

4. Nullish

특징: null 또는 undefined 값을 나타내는 타입입니다.

type Nullish = null | undefined;

5. NonNullableKeys

특징: 객체에서 값이 nullish하지 않은 키만을 걸러내는 타입입니다.

interface UserProfile {
  id: string;
  name: string | null;
  email?: string | null;
  bio?: string;
  lastLogin: Date | null;
}

function prepareProfileUpdate<T extends object>(profile: T): Pick<T, NonNullableKeys<T>> {
  const updatePayload: Partial<T> = {};
  (Object.keys(profile) as Array<keyof T>).forEach(key => {
    const isValuePresent = profile[key] !== null && profile[key] !== undefined;
    if (isValuePresent) {
      updatePayload[key] = profile[key];
    }
  });
  return updatePayload as Pick<T, NonNullableKeys<T>>;
}

const userProfileUpdate: UserProfile = {
  id: "123",
  name: "John Doe",
  email: null,
  bio: "Software Developer",
  lastLogin: null,
};

const validProfileUpdate = prepareProfileUpdate(userProfileUpdate);
// Output: { id: "123", name: "John Doe", bio: "Software Developer" }
console.log(validProfileUpdate);

구현 방법

type NonNullableKeys<T> = {
  [K in keyof T]: T[K] extends Nullish ? never : K
}[keyof T];
drizzle96 commented 2 months ago

기본 유틸리티 타입은 예전에 문제 낼 때 다뤘어서(링크-문제/답변) 커스텀 유틸리티 타입을 몇 개 찾아봤습니다~!

  1. Primitive 자바스크립트의 원시 데이터 타입만 모아둔 타입

    type Primitive = string | number | bigint | boolean | symbol | null | undefined;
  2. Falsy 자바스크립트의 falsy 한 모든 값을 모아둔 타입

    type Falsy = false | "" | 0 | 0n | null | undefined;
  3. Truthy 2번의 falsy 값을 걸러내 truthy 값만 걸러내는 타입

    
    type Truthy<T> = T extends Falsy ? never : T;

// 예시 // Result: 1 | {} type Example = Truthy<"" | 1 | false | {} | undefined>;


4. Nullish
```ts
type Nullish = null | undefined;
  1. NonNullableKeys 객체에서 값이 nullish 하지 않은 키만 걸러내는 타입
    type NonNullableKeys<T> = { 
    [K in keyof T]: T[K] extends Nullish ? never : K 
    }[keyof T];
// 예시
interface UserProfile {
  id: string;
  name: string | null;
  email?: string | null;
  bio?: string;
  lastLogin: Date | null;
}

function prepareProfileUpdate<T extends object>(
  profile: T
): Pick<T, NonNullableKeys<T>> {
  const updatePayload: Partial<T> = {};
  (Object.keys(profile) as Array<keyof T>).forEach(key => {
    const isValuePresent = profile[key] !== null &&
                           profile[key] !== undefined;
    if (isValuePresent) {
      updatePayload[key] = profile[key];
    }
  });
  return updatePayload as Pick<T, NonNullableKeys<T>>;
}

const userProfileUpdate: UserProfile = {
  id: "123",
  name: "John Doe",
  email: null,
  bio: "Software Developer",
  lastLogin: null,
};

const validProfileUpdate = prepareProfileUpdate(
  userProfileUpdate
);
// Output:
// NonNullableKeys<userProfileUpdate> => "id" | "name" | "bio"
// { id: "123", name: "John Doe", bio: "Software Developer" }
console.log(validProfileUpdate);

참고) https://dev.to/antonzo/10-sustom-utility-types-for-typescript-projects-48pe

hyeyoonS commented 2 months ago

🤔 유틸리티 타입이 뭐지?


이부분 지난 1회독때 포기했었는데.... ... ..... ㅠ ㅠ ㅠ ㅠ다시 도전 Pick과 Pick을 활용한 Omit을 가져왔음니당,,, 유틸리티 타입만을 소개하는 것은 수학 공식 외우는 느낌(?)이어서 만드는 법도 함께 가져와 보았습니다 ,,,,,, 저 이해를 다 못해서,,,, 함께 이해해보아요,,,,

✔️ Pick<T, K>

1. Pick 특징

2. Pick 사용 예제

interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

(...)

const legacyPost: Pick<Post, "title" | "content"> = {
  title: "",
  content: "",
};

// **추출된 타입 : { title : string; content : string }**

3. Pick Type 직접 어떻게 만들지?

객체 타입을 변형하는 타입이므로 맵드 타입을 이용해 만들 수 있다.

  1. 일단 2개의 타입 변수 T와 K를 사용하는 타입이므로 다음과 같이 정의한다.

    type Pick<T, K> = any;
  2. 다음으로 T로 부터 K 프로퍼티만 뽑아낸 객체 타입을 만들어야 하므로 다음과 같이 맵드 타입으로 정의한다.

    type Pick<T, K> = {
    };
  3. 마지막으로는 K가 T의 key로만 이루어진 String Literal Union 타입임을 보장해 주어야 한다. 따라서 다음과 같이 제약을 추가한다.

    type Pick<T, K extends keyof T> = {
    };

✔️ Omit

1. Omit 특징

2. Omit 사용 예제

interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

(...)

const noTitlePost: Post = { // ❌ title 프로퍼티가 없으므로 오류 발생!
  content: "",
  tags: [],
  thumbnailURL: "",
};

//해결
//Omit으로 Post 타입에서 title 프로퍼티를 제거한 타입으로 변수의 타입을 정의해 주면 된다. 
const noTitlePost: Omit<Post, "title"> = {
  content: "",
  tags: [],
  thumbnailURL: "",
};

3. Omit Type 직접 어떻게 만들지?

  1. 먼저 2개의 타입 변수를 사용하는 제네릭 타입이므로 일단 다음과 같이 정의한다.
type Omit<T, K> = any;
  1. K가 T의 key로만 이루어진 String Literal Union 타입임을 보장해 주어야 한다. 따라서 다음과 같이 제약을 추가한다.
type Omit<T, K extends keyof T> = any;

// 위 예시에서 keyof T는 ‘title’ | ‘content’ | ‘tags’ | ‘thumbnailURL’
  1. Pick 타입을 이용해서 다음과 같이 완성한다.
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

// Pick<T, Exclude<keyof T, K>>는 Pick<Post, Exclude<'title' | 'content' | 'tags' | 'thumbnailURL' , 'title>>
// Exclude는 2개의 타입 변수를 할당받고, T로부터 K를 제거한다.
// → 결과는 Pick<Post, 'content' | 'tags' | 'thumbnailURL'>

//  K에 전달한 ‘title’이 제거된 타입을 얻을 수 있다!