В одной из прошлых статей разбирал реализацию функции мемоизации на JavaScript и упомянул, что типов там нет осознанно. В этой статье мы отдельно разберём типизацию такой функции, это будет полный разбор для самых маленьких.
Шпаргалка по используемым в посте utility-типам:
typescript1Parameters<T> // возвращает тип аргументов функции T 2ReturnType<T> // возвращает тип возвращаемых значений функции T 3 4const foo = (a: number , b: number) => 5 5 6type Args = Parameters<typeof foo> 7// [a: number, b: number] 8 9type Return = ReturnType<typeof foo> 10// number 11
Итак, наша функция принимает в себя другую функцию. Сразу же типизируем это:
typescript1function memoize(func: (...args: any[]) => any) { 2 // ... 3} 4
Далее было бы неплохо типизировать возвращаемую функцию.
Для этого мы можем использовать следующую конструкцию:
typescript1(...args: Parameters<оригинальная функция>) => ReturnType<оригинальная функция> 2
И вот мы сталкиваемся с проблемой, что для корректной типизации возвращаемой функции нам необходимы типы Parameters и ReturnType, которые обязательно принимают дженерик. В качестве дженерика выступает тип оригинальной функции. Для удобства вынесем тип оригинальной функции в дженерик функции memoize:
typescript1function memoize<T extends (...args: any[]) => any>(func: T) { 2 // ... 3} 4
И итоговый интерфейс функции у нас сильно растянется, но будет таким:
typescript1type AnyFunction = (...args: any[]) => any; 2type MemoizedFunction<T extends AnyFunction> = 3 (...args: Parameters<T>) => ReturnType<T> 4 5function memoize<T extends AnyFunction>(func: T): MemoizedFunction { 6 // ... 7} 8
Ребята, стараюсь максимально упростить. Если плохо понимаете дженерики, постарайтесь более внимательно вчитываться в код.
Далее типизация кэша, тут всё просто:
typescript1const cache = new Map<string, ReturnType<T>>(); 2
По типизации, ключ — строка, а значение — тот тип, который возвращает оригинальная функция.
Далее рассмотрим итоговый код:
typescript1type AnyFunction = (...args: any[]) => any; 2type MemoizedFunction<T extends AnyFunction> = 3 (...args: Parameters<T>) => ReturnType<T> 4 5function memoize<T extends AnyFunction>(func: T): MemoizedFunction { 6 const cache = new Map<string, ReturnType<T>>(); 7 8 return function(...args: Parameters<T>): ReturnType<T> { 9 const key = JSON.stringify(args); 10 11 if (!cache.has(key)) { 12 cache.set(key, func(...args)); 13 } 14 15 return cache.get(key)!; 16 }; 17} 18
Статья была полезной?
Читайте также:
— 3 минуты
Составные компоненты
Есть такой паттерн для реакта, который называется Compound Components. Это...
— 2 минуты
Определяющие тайпгарды
Как я и писал в одном из своих постов в телеге, тайпгарды есть двух видов:...
— 3 минуты
Сужение типов и уточняющие тайпгарды
Часто в TypeScript коде можно столкнуться с тем, что типы недостаточно точн...