How to create a wrapper for SolidJS's setStore in TypeScript?

52 Views Asked by At

I have a SolidJS store defined:

const [store, setStore] = createStore<SomeComplexType>();

I need to run some code before every execution of setStore, so I would like to wrap it with my own function. Is there a way to do that that preserves all types and all overloads?

I've tried:

const setStore: SetStoreFunction<SomeComplexType> = (...args: Parameters<SetStoreFunction<SomeComplexType>>) => {
  setStore_(...args);
};

But I get an error:

A spread argument must either have a tuple type or be passed to a rest parameter.

2

There are 2 best solutions below

0
Draex_ On BEST ANSWER

The best solution I heard is using any[] for the parameter type and casting setStore to any. It preserves types and doesn't require suppression via annotations.

TS Playground

import { createStore, type SetStoreFunction } from "solid-js/store";

type Foo = { foo: string };

const [store, setStore] = createStore<Foo>({ foo: "bar" });

const setStoreWithEffect: SetStoreFunction<Foo> = (...params: any[]) => {
  console.log("setStore invoked with parameters:", params);
  return (setStore as any)(...params);
};

Credits to Maciek50322.

0
jsejcksn On

In order to preserve all of the overload information in the associated function type… you can annotate a closure that executes your effect prior to invoking setStore and returning its value.

The particular type in question has numerous complex overloads, and I don't know that it's possible to satisfy all of them as far as the TypeScript compiler can currently infer (although I'd love to learn otherwise). A // @ts-expect-error directive comment can be used to suppress the compiler error diagnostic in the following example:

TS Playground

import { createStore, type SetStoreFunction } from "solid-js/store";

type Foo = { foo: string };

const [store, setStore] = createStore<Foo>({ foo: "bar" });

// @ts-expect-error: Not assignable…
const setStoreWithEffect: SetStoreFunction<Foo> = (...params: [any]) => {
  console.log("setStore invoked with parameters:", params);
  return setStore(...params);
};

The IntelliSense quick info shows that the type information is preserved and the function can be used as expected:

typescript intellisense quick info

Quick info text in the screenshot above:

     setStoreWithEffect(k1: Part<Foo, "foo">, k2:
     Part<never, never>, k3: Part<never, never>, k4:
     Part<never, never>, k5: Part<never, never>, k6:
 ⌃   Part<never, never>, k7: Part<never, never>, setter:
1/9  (prevState: never, traversed: [never, never, never,
 ⌄   never, never, never, "foo"]) => never): void