Skip to content

Conversation

@MrSwitch
Copy link
Contributor

@MrSwitch MrSwitch commented Feb 2, 2023

Migrate to TS

@MrSwitch MrSwitch self-assigned this Feb 2, 2023
@MrSwitch MrSwitch requested a review from a team February 2, 2023 14:08
src/index.ts Outdated
/* eslint-disable @typescript-eslint/ban-types */
interface MemoizeOptions {
// useCached: Will return the last resolved cached value
useCached?: true | false | Function;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: How do i say that this Function would be called with a single parameter matching CacheItem?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would (param: CacheItem) => void work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Mejsun 👍

src/index.ts Outdated
getKey?: Function;

// cache: Caching Map
cache?: Map<string | number | symbol, any>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Map key definition seems a bit verbose, and possibly incomplete, does anyone know a better way?

src/index.ts Outdated

// getKey: Default key definition
// eslint-disable-next-line @typescript-eslint/ban-types
getKey?: Function;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function should define any function which takes an an unknown number and type of parameters. And returns something that can be used as a Map Key. How would i define such a thing... and would it be just as flexible as Function here?

src/index.ts Outdated
* @returns {Promise} Value of the callback
*/
return async (...args) => {
return async (...args: any) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: How do i remove the any here?

I want it to infer the parameter Types which are acceptable by the other parameter function callback.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need a generic for TS to infer this.

Define the memoise function like so:

export default function Memoize<ParameterTypes extends Array<unknown>>(
	callback: (...args: ParameterTypes) => unknown,
	opts: MemoizeOptions = {}
) {

Then you can use the ParameterTypes generic here:

Suggested change
return async (...args: any) => {
return async (...args: ParameterTypes) => {

Might also be worth replacing all the anys in this file with unknown, TS is stricter about what you can do with that, and if you don't really care about those types, it's just as good.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to keep the separate MemoizeCallback type, define it like so and pass through the generic:

type MemoizeCallback<ParameterTypes extends Array<unknown>> = (
	...args: ParameterTypes
) => unknown;

export default function Memoize<ParameterTypes extends Array<unknown>>(
	callback: MemoizeCallback<ParameterTypes>,
	opts: MemoizeOptions = {}
) {

But I'd argue it's not worth it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing I just thought about – it might be nice to be able to work with the return type of the callback, eg. like so:

export default function Memoize<
	ParameterTypes extends Array<unknown>,
	CallbackValue extends Promise<unknown>
>(
	callback: (...args: ParameterTypes) => CallbackValue,
	opts: MemoizeOptions<ParameterTypes, CallbackValue> = {}
) {

Though this implies the function can only be used with async functions, not sure if that is intended.

In any case this will make sure that the memoised function keeps its return type.

src/index.ts Outdated
Comment on lines 20 to 28
interface MemoizeOptions {
// useCached: Will return the last resolved cached value
useCached?: true | false | MemoizeUseCacheHandler;

// staleInMs: How long before we should check for new updates
staleInMs?: number;

// getKey: Default key definition
getKey?: (...args: any) => MemoizeCacheKey;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also weave the ParameterTypes from my earlier suggestion through to the options and use it there:

Suggested change
interface MemoizeOptions {
// useCached: Will return the last resolved cached value
useCached?: true | false | MemoizeUseCacheHandler;
// staleInMs: How long before we should check for new updates
staleInMs?: number;
// getKey: Default key definition
getKey?: (...args: any) => MemoizeCacheKey;
interface MemoizeOptions<ParameterTypes extends Array<unknown>> {
// useCached: Will return the last resolved cached value
useCached?: true | false | MemoizeUseCacheHandler;
// staleInMs: How long before we should check for new updates
staleInMs?: number;
// getKey: Default key definition
getKey?: (...args: ParameterTypes) => MemoizeCacheKey;

src/index.ts Outdated
// cache Max Size
cacheMaxSize = 1000,
} = opts;
}: MemoizeOptions = opts;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TS should be able to infer this even if you remove it.

src/index.ts Outdated
return async (...args) => {
return async (...args: any) => {
// Serialize the keys...
const key = getKey(...args);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly, TS flags this with the error A spread argument must either have a tuple type or be passed to a rest parameter. And it seems to have a point: If the callback function has multiple parameters, does it make sense to pass those to e.g. JSON.stringify as individual params?

Maybe they should be passed like so?

Suggested change
const key = getKey(...args);
const key = getKey(args);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried your implementation... this certainly proves a sticking point. I would like to to have the same parameter structure as the callback function. But yes, Typescript is happier if i pass this through as an array of the first parameter

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, maybe there's a way to shut up that TS error.

@MrSwitch MrSwitch merged commit 52fa531 into main Mar 17, 2023
@MrSwitch MrSwitch deleted the typescript branch March 17, 2023 12:48
5app-Machine added a commit that referenced this pull request Mar 17, 2023
# [2.0.0](v1.4.7...v2.0.0) (2023-03-17)

### Features

* **typescript:** migrate to typescript ([#56](#56)) ([52fa531](52fa531))

### BREAKING CHANGES

* **typescript:** this changes the getKey parameters 😭

* chore(deps): update deps

* feat(ts): add callback values, noissue

* chore: rm editorconfig

* chore(lint): fix lint

* chore(ts): remove deprecation warnings

BREAKING CHANGES: Change in the `options.getKey(...args)` => `options.getKeys(args)` args is contained in first parameter.
@5app-Machine
Copy link
Contributor

🎉 This PR is included in version 2.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants