Generics
은 유용하다// Bad
type APIResponse = {
items?: any[];
item: any;
}
// Good
type APIResponse<T> = {
items?: T[];
item: T;
}
type User = {
id: string;
}
const fetchCall = <T>(method: string, url: string): Promise<APIResponse<T>> => {
// ...
}
export const getUser = (id: string): Promise<User> => {
return fetchCall<User>(HttpMethod.GET, `/v1/user/${id}`)
.then(({ item }) => item)
}
keyof
를 사용함으로써 유니온타입을 따로 지정안해줘도 된다.type ExpiryDateTime = {
days: number;
hours: number;
minutes: number;
}
const expiryDateTime: ExpiryDateTime = {
days: 0,
hours: 0,
minutes: 0,
}
const onChange = (
key: keyof ExpiryDateTime, // days | hours | minutes
val: number
) : void = {
expiryDateTime[key] = val
}
typeof
는 변수의 타입을 추출 가능JavaScript의 typeof
와 달리 TypeScript의 typeof
는 type에서 사용하여 type으로 참조할 수 있습니다.let foo = "bar";
let name: typeof foo; // string
const getHisOrHer = (val: number): "his" | "her" => {
// ...
}
type HisOrHer = ReturnType<typeof getHisOrHer> // "his" | "her"
// OR
type GetHisOrHerFunc = (val: number) => "his" | "her"
const getHisOrHer: GetHisOrHerFunc = (val) => {
// ...
}
type HisOrHer = ReturnType<GetHisOrHerFunc> // "his" | "her"
- getHisOrHer 반환 타입을 변경하면 HisOrHer 타입이 변경 사항을 따르게 됩니다.
- 일회성 작업입니다.
type NumberId = {
id: number;
};
type StringId = {
id: string;
};
type StringOrNumber<T extends string | number> = T extends string
? StringId
: NumberId
const getId = <T extends string | number>(val: T): StringOrNumber<T> => {
//...
}
const s = getId("1")
// 문자열을 인수로 사용하면 StringId 타입 사용
const n = getId(2)
// 문자열을 숫자로 사용하면 NumberId 타입 사용
type Locale = "zh" | "en" | "ms";
type Lang = `lang-${Locale}`;
// type Lang = "lang-zh" | "lang-en" | "lang-ms"
Last but not least, the utility types:
Pick<T, Keys>
— Picking properties of key
from the typeOmit<T, Keys>
— The opposite of Pick
, it takes all properties except KeysPartial<T>
— Similar to ?
a.k.a optional
, but you don’t have to make all the properties inside to be optional, because you might only need partial in a variable or function.Required<T>
— The opposite of Partial<T>
, this will require all the properties include the optional oneReadonly<T>
— The whole property would be immutable, and read-onlyRecord<Key, T>
— I doesn’t recommend the use of object
type in TypeScript, you will often get TS2339: Property 'x' does not exist on type 'object'
. I always use Record<Key, T>
as the replacement.type TodoWithId = {
readonly id: string;
title: string;
subtitle: null | string;
description: string;
}
// Pick<T, Keys>
type Todo = Pick<TodoWithId, "title" | "subtitle" | "description">;
// Or
// Omit<T, Keys>
type Todo = Omit<TodoWithId, "id">
const t1: Todo = {
title: "foo",
subtitle: "bar",
description: "",
};
// Partial<T>
const updateTodo = (todo: Todo, update: Partial<Todo>): Todo => {
// ...
}
const t2 = updateTodo(t1, { description: "hello world" });
// Required<T>
const resetTodo = (todo: Todo, newTodo: Required<Todo>): Todo => {
// ...
}
const t3 = resetTodo(t1, { description: "hello world" });
// Type '{ description: string; }' is missing the following properties from type 'Required<Todo>': title, subtitle
// Readonly<T>
const immutableTodo: Readonly<Todo> = t2;
immutableTodo.subtitle = ""
// TS2540: Cannot assign to 'subtitle' because it is a read-only property.
// Record<Key, T>
const obj: Record<string, Todo> = {
t1: t1,
t2: t2,
};