TypeScript는 일반적인 타입 변환을 쉽게 하기 위해서 몇 가지 유틸리티 타입을 제공한다. 이러한 유틸리티는 전역으로 사용할 수 있다.
<Type>Type의 모든 프로퍼티를 선택적으로 만드는 타입을 생성한다. 이 유틸리티는 주어진 타입의 모든 하위 타입 집합을 나타내는 타입을 반환한다.
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',
});
Partial은 Type의 프로퍼티 중 일부분을 가지고 있다는 뜻이다. Type의 모든 프로퍼티를 가지거나 아무 프로퍼티를 가지지 않아도 에러가 발생하지 않지만, Type의 프로퍼티가 아닌 프로퍼티를 가지고 있다면 에러가 발생한다.
<Type>Type의 모든 프로퍼티가 필수로 설정된 타입을 생성한다. Partial의 반대다.
interface Props {
a?: number;
b?: string;
}
const obj: Props = { a: 5 };
const obj2: Required<Props> = { a: 5 }; // Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.
Props는 모든 프로퍼티가 선택적 프로퍼티지만 Required로 만들어진 타입은 모든 프로퍼티가 필수로 설정되기 때문에 obj2에서 에러가 발생한다.
<Type>Type 집합의 몯느 프로퍼티를 읽기 전용(readonly)으로 설정한 타입을 생성한다. 즉, 생성된 타입의 프로퍼티는 재할당될 수 없다.
interface Todo {
title: string;
}
const todo: Readonly<Todo> = {
title: 'Delete inactive users',
};
todo.title = 'Hello'; // Cannot assign to 'title' because it is a read-only property.
Type의 프로퍼티 key의 집합으로 타입을 생성한다. 이 유틸리티는 타입의 프로퍼티를 다른 타입에 매핑시키는데 사용될 수 있다.
interface PageInfo {
title: string;
}
type Page = 'home' | 'about' | 'contact';
const x: Record<Page, PageInfo> = {
about: { title: 'about' },
contact: { title: 'contact' },
home: { title: 'home' },
};
Type을 프로퍼티로 가지는 key를 프로퍼티로 가지는 타입을 만든다.
위 코드에서 Page를 프로퍼티로 가지는 타입을 만든다. 이 Page는 PageInfo의 프로퍼티를 가지고 있다. 그래서 x는 about, contact, home을 프로퍼티로 가져야 하과, about, contatct, home은 title 프로퍼티를 가져야 한다.
하나라도 가지고 있지 않거나 다른 프로퍼티를 쓴다면 에러가 발생한다.
Type에서 프로퍼티 Keys의 집합을 선택해 타입을 생성한다.
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, 'title' | 'completed'>;
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
};
Type의 프로퍼티 중 Keys만 선택한 타입을 만든다. Pick<Todo, 'title' | 'completed'>은 Todo의 프로퍼티 중에서 title, completed만 골라 선택한 타입을 의미한다.
Type의 모든 프로퍼티를 선택하고 선택한 Keys를 제거한 타입을 생성한다.
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Omit<Todo, 'description'>;
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
};
todos는 Todo에서 description 프로퍼티만 없는 타입이다.
Type에서 ExcludedUnion에 할당할 수 있는 모든 프로퍼티를 제외하여 타입을 생성한다.
type T0 = Exclude<'a' | 'b' | 'c', 'a'>; // 'b' | 'c'
type T1 = Exclude<'a' | 'b' | 'c', 'a' | 'b'>; // 'c'
type T2 = Exclude<string | number | (() => void), Function<; // string | number
Type 프로퍼티 중에서 ExcludedUnion 프로퍼티를 제외한 타입을 만든다.
Type에서 Union에 할당할 수 있는 모든 프로퍼티를 가져와서 타입을 생성한다.
type T0 = Extract<'a' | 'b' | 'c', 'a' | 'f'>; // 'a'
type T1 = Extract<string | number | (() => void), Function>; // () => void
<Type>Type에서 null과 undefined을 제외하고 타입을 생성한다.
type T0 = NonNullable<string | number | undefined>; // string | number
type T1 = NonNullable<string[] | null | undefined>; // string[]
<Type>함수 타입 Type의 매개변수에 사용된 타입들의 튜플 타입을 생성한다.
declare function f1(arg: { a: number, b: string }): void;
type T0 = Parameters<() => string>; // []
type T1 = Parameters<(s: string) => void>; // [s: string]
type T2 = Parameters<<T>(arg: T) => T>; // [arg: unknown]
type T3 = Parameters<typeof f1>; // [arg: { a: number, b: string }]
type T4 = Parameters<any>; // unknown[]
type T5 = Parameters<never>; // never
type T6 = Parameters<string>; // Type 'string' does not satisfy the constraint '(...args: any) => any'.
type T7 = Parameters<Function>; // Type 'Function' does not satisfy the constraint '(...args: any) => any'.
// Type 'Function' provides no match for the signature '(...args: any): any'.
<Type>생성자 함수 타입의 타입에서 튜플 또는 배열 타입을 생성한다. 모든 매개변수 타입을 가지는 튜플 타입을 생성한다.
type T0 = ConstructorParameters<ErrorConstructor>; // [message?: string]
type T1 = ConstructorParameters<FunctionConstructor>; // string[]
type T2 = ConstructorParameters<RegExpConstructor>; // pattern: strin | RegExp, flags?: string]
type T3 = ConstructorParameters<any>; // unknown[]
type T4 = ConstructorParameters<Function>; // Type 'Function' does not satisfy the constraint 'abstract new (...args: any) => any'.
// Type 'Function' provides no match for the signature 'new (...args: any): any'.
<Type>함수 Type의 반환 타입으로 구성된 타입을 생성한다.
declare function f1(): { a: number; b: string };
type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<(s: string) => void>; // void
type T2 = ReturnType<<T>() => T>; // unknown
type T3 = ReturnType<<T extends U, U extends number[]>() => T>; // number[]
type T4 = ReturnType<typeof f1>; // { a: number; b: string; }
type T5 = ReturnType<any>; //any
type T6 = ReturnType<never>; // any
type T7 = ReturnType<string>; // Type 'string' does not satisfy the constraint '(...args: any) => any'.
type T8 = ReturnType<Function>; // Type 'Function' does not satisfy the constraint '(...args: any) => any'.
//Type 'Function' provides no match for the signature '(...args: any): any'.
<Type>Type의 생성자 함수의 인스턴스 타입으로 구성된 타입을 생성한다.
class C {
x = 0;
y = 0;
}
type T0 = InstanceType<typeof C>; // C
type T1 = InstanceType<any>; // any
type T2 = InstanceType<never>; // never
type T3 = InstanceType<string>; // Type 'string' does not satisfy the constraint 'abstract new (...args: any) => any'.
type T4 = InstanceType<Function>; // Type 'Function' does not satisfy the constraint 'abstract new (...args: any) => any'.
// Type 'Function' provides no match for the signature 'new (...args: any): any'.
<Type>함수 타입의 this 매개변수의 타입, 또는 함수 타입에 this 매개변수가 없으면 unknown을 추출한다.
function toHex(this: Number) {
return this.toString(16);
}
function numberToString(n: ThisParameterType<typeof toHex>) {
return toHex.apply(n);
}
<Type>Type에서 this 매개변수를 제거한다. Type에 명시적으로 선언된 this 매개변수가 없는 경우에는 단순히 Type이다. 반면에 this 매개변수가 없는 새로운 함수 타입은 Type에서 생성된다. 제네릭은 사라지고 마지막 오버로드 시그니처만 새로운 함수 타입으로 전파된다.
function toHex(this: Number) {
return this.toString(16);
}
const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);
console.log(fiveToHex(());
<Type>이 유틸리티는 변형된 타입을 반환하지 않는다. 대신, 문맥적 this타입에 표시하는 역할을 한다. 이 유틸리티를 사용하기 위해서는 --noImplicitThis 플래그를 사용해야 한다.
type ObjectDescriptor<D, M> = {
data?: D;
methods?: M & ThisType<D & M>;
};
function makeObject<D, M>(desc: ObjectDescriptor<D, M>: D & M {
let data: object = desc.data || {};
let methods: object = desc.methods || {};
return { ...data, ...methods } as D & M;
}
let obj = makeObject({
data: { x: 0, y: 0 },
methods: {
moveBy(dx: number, dy: number) {
this.x += dx;
this.y += dy;
},
},
});
obj.x = 10;
obj.y = 20;
obj.moveBy(5, 5);
makeObject의 인수인 methos 객체는 ThisType<D & M>을 포함한 문맥적 타입을 가지고 따라서 methods 객체의 메서드 안에 this 타입은 { x: number, y: number } & { moveBy(dx: number, dy: number): number } 이다. methods 프로퍼티의 타입은 추론 대상인 동시에 메서드의 this 타입의 출처다.
ThisType<T> 마커 인터페이스는 단지 lib.d.ts에 선언된 빈 인터페이스다. 객체 리터럴의 문맥적 타입으로 인식되는 것을 넘어, 그 인터페이스는 빈 인터페이스처럼 동작한다.
탬플릿 문자열 리터럴에서의 문자열 조작을 돕기 위해, TypeScript는 타입 시스템 내에서 문자열 조작에 사용할 수 있는 타입 집합이 포함되어 있다.
<StringType>문자열의 각 문자를 대문자로 변환한다.
type Greeting = 'Hello, world';
type ShoutyGreeting = Uppercase<Greeting>; // 'HELLO, WORLD!'
type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`
type MainID = ASCIICacheKey<"my_app">
<StringType>문자열의 각 문자를 소문자로 변환한다.
type Greeting = 'Hello, world';
type QuietGreeting = Lowercase<Greeting>; // 'hello, world'
type ASCIICacheKey<Str extends string> = `id-${Lowercase<Str>}`;
type MainID = ASCIICacheKey<"MY_APP">; // 'id-my_app'
<StringType>문자열의 첫 문자를 대문자로 변환한다.
type LowercaseGreeting = 'hello, world';
type Greeting = Capitalize<LowercaseGreeting>; // 'Hello, world'
<StringType>문자열의 첫 문자를 소문자로 변환한다.
type UppercaseGreeting = "HELLO, WORLD';
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>; // 'hELLO, WORLD'