[ts] typescript utility type
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