4 Typescript 제네릭 함수의 힘을 활용하는 방법에 대한 아이디어
제네릭 함수는 아마도 가장 까다롭지만 가장 강력한 Typescript 개념 중 하나일 것입니다. 제네릭이라는 주제를 간략하게 다루었지만 이제 더 깊이 파고들어 제네릭의 힘을 활용하여 확장 가능하고 재사용 가능한 코드를 제공할 수 있는 방법에 대해 이야기하고 싶습니다. 오늘 우리는 ❤️로 만들고 Typescript로 구동되는 일반 도우미 함수에 대한 네 가지 아이디어를 고려할 것입니다.
부인 성명
다양한 방법으로 궁극적인 솔루션을 찾고 있다면 ramda 또는 lodash 과 같은 기존 라이브러리를 확인하는 데 관심이 있을 수 있습니다. 이 게시물의 목적은 일상적인 개발에 유용하고 Typescript 제네릭의 그림에 적합한 몇 가지 예를 논의하는 것입니다. 의견에 자유롭게 사용 사례를 추가하고 함께 토론합시다 💪
내용의 테이블
시작하기 전에
예를 들어 두 개의 간단한 인터페이스를 생각해 내고 그 인터페이스에서 배열을 만들었습니다.
interface Book {
id: number;
author: string;
}
interface Recipe {
id: number;
cookingTime: number;
ingredients: string[];
}
const books: Book[] = [
{ id: 1, author: "A" },
{ id: 2, author: "A" },
{ id: 3, author: "C" }
]
const recipes: Recipe[] = [
{ id: 1, cookingTime: 10, ingredients: ["salad"] },
{ id: 2, cookingTime: 30, ingredients: ["meat"] }
]
1. 키로 매핑
interface Item<T = any> {
[key: string]: T
}
function mapByKey<T extends Item>(array: T[], key: keyof T): Item<T> {
return array.reduce((map, item) => ({...map, [item[key]]: item}), {})
}
Let's look closer to what happens here:
-
interface Item<T = any> { ... }
is a generic interface, with a default value ofany
(yes you can have default values in generics 🚀) -
<T extends Item>(array: T[], key: keyof T)
: TypeT
is inferred from the parameter, but it must satisfy the condition<T extends Item>
(in other wordsT
must be an object). -
key: keyof T
second parameter is constrained to the keys which are only available inT
. If we are usingBook
, then available keys areid | author
. -
(...): Item<T>
is a definition of the return type: key-value pairs, where values are of typeT
Let's try it in action:
mapByKey(books, "wrongKey") // error. Not keyof T -> (not key of Book)
mapByKey(books, "id") // {"1":{"id":1,"author":"A"},"2":{"id":2,"author":"A"},"3":{"id":3,"author":"C"}}
As you can see, we can now benefit from knowing in advance available keys. They are automatically inferred from the type of the first argument. Warning: this helper is handy with unique values like ids; however, if you have non-unique values, you might end up overwriting a value which was previously stored for that key.
2. 키로 그룹화
This method is beneficial, if you need to aggregate data based on a particular key, for instance, by author name.
We start by creating a new interface, which will define our expected output.
interface ItemGroup<T> {
[key: string]: T[];
}
function groupByKey<T extends Item>(array: T[], key: keyof T): ItemGroup<T> {
return array.reduce<ItemGroup<T>>((map, item) => {
const itemKey = item[key]
if(map[itemKey]) {
map[itemKey].push(item);
} else {
map[itemKey] = [item]
}
return map
}, {})
}
It's interesting to note, that Array.prototype.reduce
is a generic function on its own, so you can specify the expected return type of the reduce to have better typing support.
In this example, we are using the same trick with keyof T
which under the hood resolves into the union type of available keys.
groupByKey(books, "randomString") // error. Not keyof T -> (not key of Book)
groupByKey(books, "author") // {"A":[{"id":1,"author":"A"},{"id":2,"author":"A"}],"C":[{"id":3,"author":"C"}]}
3. 병합
function merge<T extends Item, K extends Item>(a: T, b: K): T & K {
return {...a, ...b};
}
In the merge example T & K
is an intersection type. That means that the returned type will have keys from both T
and K
.
const result = merge(books[0], recipes[0]) // {"id":1,"author":"A","cookingTime":10,"ingredients":["bread"]}
result.author // "A"
result.randomKey // error
4. 정렬
What is the problem with Array.prototype.sort
method? → It mutates the initial array. Therefore I decided to suggest a more flexible implementation of the sorting function, which would return a new array.
type ValueGetter<T = any> = (item: T) => string | number;
type SortingOrder = "ascending" | "descending";
function sortBy<T extends Item>(array: T[], key: ValueGetter<T>, order: SortingOrder = "ascending") {
if(order === "ascending") {
return [...array].sort((a, b) => key(a) > key(b) ? 1 : -1 )
}
return [...array].sort((a, b) => key(a) > key(b) ? -1 : 1 )
}
We will use a ValueGetter
generic function, which will return a primitive type: string or number. It is a very flexible solution because it allows us to deal with nested objects efficiently.
// Sort by author
sortBy(books, (item) => item.author, "descending")
// Sort by number of ingredients
sortBy(recipes, (item) => item.ingredients.length)
// Sort very nested objects
const arrayOfNestedObjects = [{ level1: { level2: { name: 'A' } } }]
sortBy(arrayOfNestedObjects, (item) => item.level1.level2.name)
요약
이 포스트에서 우리는 JS 배열과 객체에 대한 일반적인 작업을 위한 도우미 함수를 작성하여 Typescript의 일반 함수를 가지고 놀았습니다. Typescript는 재사용 가능하고 구성 가능하며 유형이 안전한 코드를 생성할 수 있는 다양한 도구를 제공합니다.
제 게시물이 마음에 드셨다면 널리 퍼트려주시고 🚀웹 개발에 관한 더 흥미로운 콘텐츠를 만나보세요.
Reference
이 문제에 관하여(4 Typescript 제네릭 함수의 힘을 활용하는 방법에 대한 아이디어), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/glebirovich/4-ideas-of-how-to-harness-the-power-of-typescript-generic-function-2b62텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)