개발/javascript

[ts] typescript utility type

방푸린 2022. 7. 5. 17:37
반응형

https://www.typescriptlang.org/docs/handbook/utility-types.html

 

Documentation - Utility Types

Types which are globally included in TypeScript

www.typescriptlang.org

위 원문을 번역한다.

 

Partial<Type>

: 특정 object의 일부분만 가질 때 사용한다. 항목을 지정하지 않고 그저 일부이면 가능.

interface Todo {
  title: string;
  description: string;
}
 
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
  return { ...todo, ...fieldsToUpdate };
}
 
const todo1 = {
  title: "organize desk",
  description: "clear clutter",
};
 
const todo2 = updateTodo(todo1, {
  description: "throw out trash",
});


console.log(updateTodo(todo1, todo2));

===========

[LOG]: {
  "title": "organize desk",
  "description": "throw out trash"
}

위 소스에서 Partial<Todo>는 Todo 인터페이스가 가진 항목의 일부를 가진 object이다.

 

Required<Type>

: 특정 object의 모든 항목을 반드시 가져야 한다.

// @errors: 2741
interface Props {
  a?: number;
  b?: string;
}

const obj: Props = { a: 5 };

const obj2: Required<Props> = { a: 5 };

==================
Errors in code
Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.

위 소스에서 Required<Props>로 선언된 obj2는 optional로 선언한 b항목이 없어서 에러가 난다.

 

Readonly<Type>

: 처음 선언된 값을 변경할 수 없다.

// @errors: 2540
interface Todo {
  title: string;
}

const todo: Readonly<Todo> = {
  title: "Delete inactive users",
};

todo.title = "Hello";

================
Errors in code
Cannot assign to 'title' because it is a read-only property.

위 소스에서 Readonly<Todo> todo로 선언되어 있기 때문에 todo안의 항목의 값을 바꾸려고 할 경우 에러가 난다.

이 타입은 Object.freeze 함수 내부에서 사용되고 있다.

function freeze<Type>(obj: Type): Readonly<Type>;

----------------

const obj = {
  prop: 42
};

Object.freeze(obj); //freeze시키면

obj.prop = 33; //값을 바꿔도
// Throws an error in strict mode

console.log(obj.prop); //안 바뀐다
// expected output: 42

 

Record<Keys, Type>

: 키가 Key이고 값이 Type인 객체 타입

interface CatInfo {
  age: number;
  breed: string;
}

type CatName = "miffy" | "boris" | "mordred";

const cats: Record<CatName, CatInfo> = {
  miffy: { age: 10, breed: "Persian" },
  boris: { age: 5, breed: "Maine Coon" },
  mordred: { age: 16, breed: "British Shorthair" },
};

console.log(cats.boris);

==============
[LOG]: {
  "age": 5,
  "breed": "Maine Coon"
}

참고로 Record는 인덱스 시그니처(Index Signature)는 대괄호로 객체를 접근하는 방식으로도 표현가능하다.

interface CatInfo {
  age: number;
  breed: string;
}

type CatName = "miffy" | "boris" | "mordred";

const cats: Record<CatName, CatInfo> = {
  miffy: { age: 10, breed: "Persian" },
  boris: { age: 5, breed: "Maine Coon" },
  mordred: { age: 16, breed: "British Shorthair" },
};

const catss: {[name: string] : CatInfo} = { // index signature
   boris: { age: 5, breed: "Maine Coon" },
}

console.log(cats.boris);
console.log(catss.boris);

다만 인덱스 시그니처는 문자열 리터럴을 Key로 사용하는 경우 오류가 발생한다. 위 catss를 `catss: {[name: CatName : CatInfo}` 로 선언하면 에러가 난다.

 

Pick<Type, Keys>

: 어떤 object의 항목 중 일부만 골라서 사용 가능하다. 비슷한 부분집합 형태의 interface를 또 만들 필요가 없어진다.

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

선택한 항목 외에 다른 것을 더 넣거나 덜 넣으면 에러가 난다.

 

Omit<Type, Keys>

: Pick과 반대 기능으로 특정 항목만 제하고 사용할 수 있다.

interface Todo {
  title: string;
  description: string;
  completed: boolean;
  createdAt: number;
}

type TodoPreview = Omit<Todo, "description">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
  createdAt: 1615544252770,
};

const todo2: TodoPreview = { // completed가 추가적으로 빠져서 에러가 난다.
  title: "Clean room",
  createdAt: 1615544252770,
};


type TodoInfo = Omit<Todo, "completed" | "createdAt">;

const todoInfo: TodoInfo = {
  title: "Pick up kids",
  description: "Kindergarten closes at 5pm",
};

제한 항목 이외의 것을 더 넣거나 덜 넣으면 에러가 난다.

 

Exclude<UnionType, ExcludedMembers>

: 앞의 것 빼기 뒤의 것을 타입으로 갖는다.

type T0 = Exclude<"a" | "b" | "c", "a">;

const test1: T0 = "a"; // 에러
const test2: T0 = "b";
const test3: T0 = "d"; // 애러

type T1 = Exclude<"a" | "b" | "c", "a" | "b">;

type T2 = Exclude<string | number | (() => void), Function>;

const test4: T2 = function(){ // 에러
    console.log("this")
}

const test6: T2 = function(a: string):string{ // 에러
    return "hello " + a;
};

type T3 = Exclude<string | number | Function, Function>;

const test5: T3 = function(){ // 에러
    console.log("this")
}

제한 타입 이외의 값을 가지면 에러가 난다.

 

Extract<Type, Union>

: 앞의 타입 중 뒤의 것만 추출한 것을 타입으로 가진다.

type T0 = Extract<"a" | "b" | "c", "a" | "f">;
const test:T0 = "f"; // 에러
const test2:T0 = "b"; // 에러
const test3:T0 = "a";

type T1 = Extract<string | number | (() => void) | ((args: string) => string), Function>;

const test4:T1 = function(a: string):string{
    return 'hello ' + a;
}

const test5:T1 = function():string{
    return 'hello';
}

const test6:T1 = function(a: string){ // 에러
    console.log("test " + a)
}

const test7:T1 = "guest" // 에러

위 소스에서 T1은 string, number, 인자 없는 함수, 인자 있고 return 있는 함수 중 함수의 형태만 타입으로 가질 수 있다.

test6은 return 이 없는 함수고 test7은 string이라 에러가 난다.

 

NonNullable<Type>

: Type에 들어오는 타입 중 null, undefined를 제한 타입만 가능하다

type T0 = NonNullable<string | number | undefined>;
// T0는 string, number만 가능

type T1 = NonNullable<string[] | null | undefined>;
// T1은 string[]만 가능

 

Parameters<Type>

: 함수의 파라미터를 타입으로 리턴한다.

declare function f1(arg: { a: number; b: string }): void;

type T0 = Parameters<() => string>;
//   T0 = []
type T1 = Parameters<(s: string) => void>;
//   T1 = [s:string]
type T2 = Parameters<<T>(arg: T) => T>;
//   T2 = [arg: unknown]
type T3 = Parameters<typeof f1>;
//   T3 = [arg: { a: number; b: string }]
type T4 = Parameters<any>;
//   T4 = unknown[]

// never는 any를 제외한 모든 타입의 원시타입이기때문에
// 함수타입 T에 never로 주어도 에러가 발생하지 않음
type T5 = Parameters<never>;
//   T5 = never

type T6 = Parameters<string>;
//    에러
type T7 = Parameters<Function>;
//    에러

위 소스에서 T6, T7은 '(...args: any) => any'의 형식이 아니라서 에러가 난다. 즉 Parameters가 인자로 받을 수 있는 것은 모든 파라미터를 인자로 받고 결괏값으로 모든 값을 리턴하는, 사실상 모든 함수가 된다.

 

ConstructorParameters<Type>

: 생성자를 갖는 함수 타입의 생성자 파라미터를 리턴한다. 함수가 아니라면 never를 리턴한다. 위 Parameters와 비슷하지만 생성자 파라미터로 한정한다.

type T0 = ConstructorParameters<ErrorConstructor>;
//   T0 = [message?: string | undefined]
type T1 = ConstructorParameters<FunctionConstructor>;
//   T1 = string[]
type T2 = ConstructorParameters<RegExpConstructor>;
//   T2 = [pattern: string | RegExp, flags?: string | undefined]
type T3 = ConstructorParameters<any>;
//   T3 = unknown[]

//에러발생
type T4 = ConstructorParameters<Function>;
//   T4 = never


============
class Person {
    private _firstname: string
    private _lastname: string

    constructor(firstname: string, lastname: string) {
        this._firstname = firstname
        this._lastname = lastname
    }
}

type typeIs = ConstructorParameters<typeof Person>;
let personConstructionArgs: typeIs = ['first', 'last']

 

ReturnType<Type>

: 함수의 리턴타입을 가져온다.

declare function f1(): { a: number; b: string };

type T0 = ReturnType<() => string>;
//   T0 = string
type T1 = ReturnType<(s: string) => void>;
//   T1 = void
type T2 = ReturnType<<T>() => T>;
//   T2 = unknown
type T3 = ReturnType<<T extends U, U extends number[]>() => T>;
//   T3 = number[]
type T4 = ReturnType<typeof f1>;
//   T4 = { a: number; b: string }
type T5 = ReturnType<any>;
//   T5 = any
type T6 = ReturnType<never>;
//   T6 = never;

//에러
type T7 = ReturnType<string>;
//   T7 = any
type T8 = ReturnType<Function>;
//   T8 = any

 

InstanceType<Type>

: 생성자로 초기화된 인스턴스 타입을 리턴한다.

class C {
  x = 0;
  y = 0;
}

type T0 = InstanceType<typeof C>;
//   T0 = C
const test :T0 = {x: 1, y: 3, z: 2} // 에러

type T1 = InstanceType<any>;
//   T1 = any
type T2 = InstanceType<never>;
//   T2 = never

// 에러
type T3 = InstanceType<string>;
//   T3 = any
type T4 = InstanceType<Function>;
//   T4 = any

 

ThisParameterType<Type>

: 함수 타입의 this 파라미터의 type을 가져온다. this를 가지지 않는 함수 타입이면 unknown을 반환한다.

function toHex(this: Number) {
  return this.toString(16);
}

type T = ThisParameterType<typeof toHex>;
//   T = Number

function numberToString(n: ThisParameterType<typeof toHex>) {
  return toHex.apply(n);
}

 

OmitThisParameter<Type>

: 타입의 this 파라미터를 무시한다. 타입에 this가 없으면 그냥 Type이다. 있으면 this가 없는 Type 이 만들어진다.

function toHex(this: Number) {
  return this.toString(16);
}
console.log(toHex.call(2)); //2

type T = OmitThisParameter<typeof toHex>;
//   T = () => string
const fiveToHex: T = toHex.bind(16);

console.log(fiveToHex()); //10

 

728x90
반응형