TypeScript 유틸리티 타입

승헌·2022년 4월 21일
0

TypeScript를 쓸 때 유틸리티 타입을 이용하면 타입 변환을 더 쉽게 할 수 있기 때문에 기억해 두면 좋다.

TypeScript Handbook의 내용을 바탕으로 정리했다.

Partial<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",
});
// 위 코드에서 todo2의 값 (todo1에 todo2 속성 업데이트)
todo2 : {
  "title": "organize desk",
  "description": "throw out trash"
}

Partial<Type>Type의 프로퍼티 중에서 일부분을 가지고 있다는 뜻이다.
Type의 모든 프로퍼티를 가지거나 아무 프로퍼티를 가지지 않아도 에러가 발생하지 않지만 Type의 프로퍼티가 아닌 프로퍼티를 가지고 있다면 에러가 발생한다.

Required<Type>

Type의 모든 프로퍼티가 필수로 설정된 타입을 생성합니다.
Partial의 반대입니다.

interface Props {
    a?: number; // 선택적 프로퍼티
    b?: string; // 선택적 프로퍼티
};

const obj: Props = { a: 5 }; // 성공

const obj2: Required<Props> = { a: 5 }; // 오류: 프로퍼티 'b'가 없습니다

Props 는 모든 프로퍼티가 선택적 프로퍼티지만 Required 로 만들어진 타입은 모든 프로퍼티가 필수로 설정되기 때문에 obj2에서 에러가 발생한다.

Readonly<Type>

Type 집합의 모든 프로퍼티를 읽기 전용(readonly)으로 설정한 타입을 생성합니다.
즉, 생성된 타입의 프로퍼티는 재할당될 수 없습니다.

interface Todo {
    title: string;
}

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

todo.title = 'Hello'; // 오류: 읽기 전용 프로퍼티에 재할당할 수 없음

위 코드에서 todo는 읽기 전용으로 설정됐기 때문에 에러가 발생한다.

이 유틸리티는 런타임에 실패할 할당 표현식을 표현할 때 유용합니다.
(예, frozen 객체 의 프로퍼티에 재할당하려고 하는 경우)

Object.freeze

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

Record<Keys,Type>

타입 Type의 프로퍼티 집합 Keys로 타입을 생성합니다.
이 유틸리티는 타입의 프로퍼티를 다른 타입에 매핑 시키는데 사용될 수 있습니다.

interface PageInfo {
    title: string;
}

type Page = 'home' | 'about' | 'contact';

const x: Record<Page, PageInfo> = {
    about: { title: 'about' },
    contact: { title: 'contact' },
    home: { title: 'home' },
};

Type을 프로퍼티로 가지는 Keys를 프로퍼티로 가지는 타입을 만든다.

위 코드에서 Page를 프로퍼티로 가지는 타입을 만든다.
근데 이 PagePageInfo의 프로퍼티를 가지고 있다.

그래서 xabout, contact, home을 프로퍼티로 가져야 하고, about, contact, hometitle 프로퍼티를 가져야 한다.
하나라도 안가지고 있거나 다른 프로퍼티를 쓴다면 에러가 발생한다.

Pick<Type,Keys>

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 만 골라 선택한 타입을 의미한다.

Omit<Type,Keys>

Type의 모든 프로퍼티를 선택하고 Keys를 제거한 타입을 생성합니다.

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

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

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

todo는 Todo에서 description 프로퍼티만 없는 타입이다.

Exclude<Type,ExcludedUnion>

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 프로퍼티를 제외한 타입을 만든다.

Extract<Type,Union>

Type에서 Union에 할당할 수 있는 모든 프로퍼티를 가져와서 타입을 생성합니다.

type T0 = Extract<"a" | "b" | "c", "a" | "f">;  // "a"
type T1 = Extract<string | number | (() => void), Function>;  // () => void

NonNullable<Type>

Type에서 nullundefined을 제외하고 타입을 생성합니다.

type T0 = NonNullable<string | number | undefined>;  // string | number
type T1 = NonNullable<string[] | null | undefined>;  // string[]

Parameters<Type>

함수 타입 Type의 매개변수에 사용된 타입들의 튜플 타입을 생성합니다.

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

type T0 = Parameters<() => string>;  // []
type T1 = Parameters<(s: string) => void>;  // [string]
type T2 = Parameters<(<T>(arg: T) => T)>;  // [unknown]
type T4 = Parameters<typeof f1>;  // [{ a: number, b: string }]
type T5 = Parameters<any>;  // unknown[]
type T6 = Parameters<never>;  // never
type T7 = Parameters<string>;  // 오류
type T8 = Parameters<Function>;  // 오류

ConstructorParameters<Type>

생성자 함수 타입의 타입에서 튜플 또는 배열 타입을 생성합니다.
모든 매개변수 타입을 가지는 튜플 타입(Type이 함수가 아닌 경우 타입 never)을 생성합니다.

type T0 = ConstructorParameters<ErrorConstructor>; // [(string | undefined)?]
type T1 = ConstructorParameters<FunctionConstructor>; // string[]
type T2 = ConstructorParameters<RegExpConstructor>; // [string, (string | undefined)?]
type T3 = ConstructorParameters<any>; // unknown[]

type T4 = ConstructorParameters<Function>; // never

T4는 에러가 발생한다.

Type 'Function' does not satisfy the constraint 'abstract new (...args: any) => any'.
Type 'Function' provides no match for the signature 'new (...args: any): any'.

ReturnType<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)>;  // {}
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 T8 = ReturnType<Function>;  // 오류

InstanceType<Type>

생성자 함수 타입 Type의 인스턴스 타입으로 구성된 타입을 생성합니다.

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

type T0 = InstanceType<typeof C>;  // C
type T1 = InstanceType<any>;  // any
type T2 = InstanceType<never>;  // any
type T3 = InstanceType<string>;  // 오류
type T4 = InstanceType<Function>;  // 오류

ThisParameterType<Type>

함수 타입의 this 매개변수의 타입, 또는 함수 타입에 this 매개변수가 없을 경우 unknown을 추출합니다.

유의: 이 타입은 --strictFunctionTypes가 활성화되었을 때만 올바르게 동작합니다. #32964를 참고하세요.

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

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

OmitThisParameter<Type>

Type에서 this 매개변수를 제거합니다.

Type에 명시적으로 선언된 this 매개변수가 없는 경우에, 단순히 Type입니다.
반면에, this 매개변수가 없는 새로운 함수 타입은 Type에서 생성됩니다.
제네릭은 사라지고 마지막 오버로드 시그니처만 새로운 함수 타입으로 전파됩니다.

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

// `bind`의 반환 타입은 이미 `OmitThisParameter`을 사용하고 있습니다, 이는 단지 예제를 위한 것입니다.
const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);

console.log(fiveToHex());

ThisType<Type>

이 유틸리티는 변형된 타입을 반환하지 않습니다. 대신, 문맥적 this 타입에 표시하는 역할을 합니다.

이 유틸리티를 사용하기 위해서는 --noImplicitThis 플래그를 사용해야 하는 것을 기억하세요.

// --noImplicitThis 로 컴파일

type ObjectDescriptor<D, M> = {
    data?: D;
    methods?: M & ThisType<D & M>;  // 메서드 안의 'this 타입은 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
            this.y += dy;  // 강하게 타입이 정해진 this
        }
    }
});

obj.x = 10;
obj.y = 20;
obj.moveBy(5, 5);

위 예제에서, makeObject의 인수인 methods 객체는 ThisType<D & M> 을 포함한 문맥적 타입을 가지고 따라서 methods 객체의 메서드 안에 this 타입은 { x: number, y: number } & { moveBy(dx: number, dy: number): number }입니다. methods 프로퍼티의 타입은 추론 대상인 동시에 메서드의 this 타입의 출처인 것에 주목하세요.

ThisType<T> 마커 인터페이스는 단지 lib.d.ts에 선언된 빈 인터페이스입니다. 객체 리터럴의 문맥적 타입으로 인식되는 것을 넘어, 그 인터페이스는 빈 인터페이스처럼 동작합니다.

내장 문자열 조작 타입

템플릿 문자열 리터럴에서의 문자열 조작을 돕기 위해, TypeScript는 타입 시스템 내에서 문자열 조작에 사용할 수 있는 타입 집합이 포함되어있다.

Uppercase<StringType>

문자열의 각 문자를 대문자로 변환합니다.

type Greeting = "Hello, world";
type ShoutyGreeting = Uppercase<Greeting>; // type ShoutyGreeting = "HELLO, WORLD"

type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`;
type MainID = ASCIICacheKey<"my_app">; // type MainID = "ID-MY_APP"

Lowercase<StringType>

문자열의 각 문자를 소문자로 변환합니다.

type Greeting = "Hello, world";
type QuietGreeting = Lowercase<Greeting>; // type QuietGreeting = "hello, world"
 
type ASCIICacheKey<Str extends string> = `id-${Lowercase<Str>}`;
type MainID = ASCIICacheKey<"MY_APP">; // type MainID = "id-my_app"

Capitalize<StringType>

문자열의 첫 문자를 대문자로 변환합니다.

type LowercaseGreeting = "hello, world";
type Greeting = Capitalize<LowercaseGreeting>; // type Greeting = "Hello, world"

Uncapitalize<StringType>

문자열의 첫 문자를 소문자로 변환합니다.

type UppercaseGreeting = "HELLO WORLD";
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>; // type UncomfortableGreeting = "hELLO WORLD"
profile
https://heony704.github.io/ 이리콤

0개의 댓글

관련 채용 정보