JavaScript & TypeScript

[TS] utility type

반응형

 

1. 타입의 기본 변경

타입스크립트에서 타입을 고정하지 않고, 타입을 변환하거나 일부 변환이 가능합니다.

 

Index type

인덱스 타입은 속성이 정해져 있지 않고 동적으로 처리해야 할 경우 사용합니다.

흔히 자바스크립트 객체에서 배우는 내용으로 아래와 같이 접근을 할 수 있습니다.

  const lol = {
    cham: 'nunu',
  };
  
  obj.name; // nunu
  obj['name']; // nunu

 

 

하지만 타입스크립트에서는 세밀하게 type을 이용하여 정의할 수 있습니다.

캐릭터 타입의 name을 이용하고 선언하여 이름을 설정하거나, 캐릭터의 스킬 값 q,r을 정의할 수도 있습니다.

즉, 타입의 선언한 내용을 필요한 것만 사용이 가능한 것입니다.

  type character = {
    name: string;
    skill: 'q' | 'r';
  };

  type Name = character['name']; // string
  const text: Name = 'nunu';

  type Skill = character['skill']; //'q' | 'r'

 

아래와 같이 character['skill'] 을 이용하여 다른 타입에 키를 불러서 값을 설정해 줄 수 있습니다.

  type Mid = {
    name: string;
    skill: character['skill'];
  };

  const zed: Mid = {
    name: 'zed',
    skill: 'q',
  };
}

 

 

keysof

type 선언의 모든 키값을 모두 넣을 때 사용하는 것 입니다.

그래서 name, skill을 유니온 타입으로 들어가여 name과 skill만 사용 가능하게 됩니다.

  type Keyof = keyof character; // 'name' | 'skill'
  const choice: Keyof = 'skill';

 

 

Mapped type

자바스크립트에서 사용하는 map을 타입에 이용하는 경우라고 생각하면 쉽습니다.

 

T 타입에 있는 모든 키들을 순차적으로 P에 할당하고 값들을 map으로 지정한 것입니다. 

for in과 동일한 문법이라 생각하면 쉽습니다.

그리고 값을 변경도 가능합니다 (zed -> ahri)

  type character = {
    name: string;
    skill: string;
  };


// for ~ in
  type Optional<T> = {
    [P in keyof T]?: T[P]; 
  };
  
  
  const mid: Optional<character> = {
    name: 'zed',
  };
  
  mid.name = 'ahri';

readonly를 사용하면 다른것으로 변경이 불가능하게 설정 가능하며 모든 타입을 쓰게 설정해 주어야 합니다.

위 코드에서는 name만 사용하였지만 아래에선 name, skill 모두 사용하였고 쓰지 않으면 에러가 생깁니다.

그리고 ReadOnly 타입에서 " | null " 도 같이 선언을 해 주었는데 그렇게 되면 타입 또는 null도 대입을 할 수가 있습니다.

  type ReadOnly<T> = {
    readonly [P in keyof T]: T[P] | null;
  };
  
   const jug: ReadOnly<character> = {
    name: 'Vi',
    skill: null,
  };
  type Proxy<T> = {
    get(): T;
    set(value: T): void;
  };

  type Proxify<T> = {
    [P in keyof T]: Proxy<T[P]>;
  };

 

Condition type

어떠한 환경에 의해서 사람의 기분(컨디션)이 달라지듯 타입에서도 어떠한 타입을 받아들이냐에 따라 다른 타입을 설정할 수가 있습니다.

아래의 Condition 타입에서 string을 받아들이면 boolean, 그렇지 않으면 number가 되는 코드 입니다.

type Condition<T> = T extends string ? boolean : number;
type Type = Check<string>; // boolean

이걸 응용해서 자유롭게 타입을 설정 가능합니다.

type TypeName<T> = T extends string
  ? 'string'
  : T extends number
  ? 'number'
  : T extends boolean
  ? 'boolean'
  : T extends undefined
  ? 'undefined'
  : T extends Function
  ? 'function'
  : 'object';

type Type1 = TypeName<string>;
// 'string'

type Type2 = TypeName<1>;
// 'number'

type Type3 = TypeName<() => void>;
// 'function'

string 타입을 받아들이면 'string'으로 되고 number는 'number', Function은 'function'이 되게 됩니다.

 

 

2. utility type

[1,2].map 이나 Math.abs를 사용하듯

타입변경을 이전(위에있는) 예시 코드 처럼 일일이 번거롭게 타입설정을 해주지 않고 사용할 수 있는 utility type이 있습니다.

 

 

1. Readonly

구조

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

 

사용

  type Character = {
    name: string,
    skill: string
  };

  const mid: Readonly<Character> = {
    name: 'zed',
    skill: 'q'
  };

Readonly는 읽기만 가능하고 값을 변경할 수 없고, 해당 키 값을 모두 사용해야 합니다.

 

 

2. Partial

구조

type Partial<T> = {
    [P in keyof T]?: T[P];
};

사용

  type Character = {
    name: string,
    skill: 'q' | 'r';
  };

// skillToUpdate에서 Partial를 사용
  function updateSkill(position: Character, skillToUpdate: Partial<Character>): Character {
    return { ...position, ...skillToUpdate };
  }

  const mid: Character = {
    name: 'zed',
    skill: 'q'
  };

// skill의 q를 r로 업데이트
  const update = updateSkill(mid, { skill: 'r' });

 

기존의 타입 중에서 부분적으로 허용하여 변경하려고 할 때 사용합니다.

 

 

3. Pick

구조

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

외부에서 받아오는것을 T, 그 중 키를 선택을 한 것을 K , 키값은 P 입니다.

 

사용

  type Character = {
    name: string;
    skill: "q" | "r";
    item: "Doran";
  };

  //Character 타입에서 'skill' 'item'을 선택하여 Pick 사용
  type SkillItem = Pick<Character, "skill" | "item">;

  // skillItem을 사용하여 타입에 지정
  function itemSkill(): SkillItem {
    return {
      skill: "q",
      item: "Doran",
    };
  }

Pick이라는 단어에 처럼 사용하고자 하는 키값만 선택할 때 사용합니다.

그리고 Pick한 키값은 모두 사용해야 합니다.

 

4. Omit

구조

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

사용

  type Character = {
    name: string;
    skill: "q" | "r";
    item: "Doran";
  };

  //Character 타입에서 'item'을 선택하여 Omit 사용
  type SkillItem = Omit<Character,  "item">;

  // skillItem을 사용하여 타입에 지정
  function notItemSkill(): SkillItem {
    return {
      name: 'zed',
      skill: "q"
    };
  }
}

Omit은 Pick의 반대 개념입니다. 선택한 것들을 제외하는 것으로

그리고 Omit 설정을 하지 않은 키값은 모두 사용해야 합니다.

 

 

5. Record

구조

type Record<K extends keyof any, T> = {
    [P in K]: T;
};

사용

  type Character = {
    name: string;
  };
  type Position = 'Mid' | 'JUG' | 'AD';


// Position과 Character을 사용한 Record
  const position: Record<Position, Character> = {
    Mid: { name: 'zed' },
    JUG: { name: 'Vi' },
    AD: { name: 'Jhin' },
  };

위 코드에서 Postion을 키, Character을 값으로 사용하여 묶을때  사용하는 Record 입니다.

Record<key, value>

 

 

6. NonNullable

구조

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

사용

  type Position = null | 'JUG' | 'AD';
  type nonNullPosition = NonNullable<Position>
    // null은 제외하여 'JUG', 'AD'만 선택 가능
    
  type Character = {
    position: nonNullPosition;
  };

  function notItemSkill(): Character {
    return {
      position: 'JUG'
      // 또는 'AD'만 가능
    };
  }

null  undefined를 제외하도록 하여 타입을 구성합니다.

 

 

 

참고 할만한 사이트

https://im-developer.tistory.com/209

https://typescript-kr.github.io/pages/utility-types.html

 

반응형

'JavaScript & TypeScript' 카테고리의 다른 글

[TS] Decorators  (0) 2022.03.23
[TS] Compile (option)  (0) 2022.03.22
[TS] type alias, interface 차이  (0) 2022.03.20
[JS] ProtoType  (0) 2022.03.15
[TS] 제네릭(Generics)  (0) 2022.03.14