简体   繁体   中英

How do I set the type, for custom store, when I pass store via setContext or component prop?

When using SvelteKit, the store must always be created in the route page / layout , otherwise there will be problems with the status being up-to-date after refreshing.
You can't create a store in an imported module, because the server-side module only loads once, and this causes errors in the store .
All this leads to a certain situation with types.

The problem:

I create and pass the store from +page.svelte to Child.svelte , via setContext :


<!-- +page.svelte -->
<script>
  import Child from "./Child.svelte";
  import { writable } from 'svelte/store';
  import { setContext } from "svelte";
  
  function createCount() {
    /** @type {import("svelte/store").Writable<number>} */
    const { subscribe, set, update } = writable(0);
    
    return {
      subscribe,
      increment: () => update(n => n + 1),
      decrement: () => update(n => n - 1),
      reset: () => set(0)
    };
  }
  
  export const count = createCount();
  
  setContext("store", count);
</script>

<Child/>
<!-- Child.svelte -->
<script>
import { getContext } from "svelte";
  
  const count = getContext("store");
</script>

<h1>The count is {$count}</h1>

<button on:click={count.increment}>+</button>
<button on:click={count.decrement}>-</button>
<button on:click={count.reset}>reset</button>

In Child.svelte the type for store count is any :
奥布拉兹

And it should be as in the place of the declaration:
奥布拉兹

That's what it should be the type of store , returned from getContext("store"):

const count: {
    subscribe: (this: void, run: Subscriber<number>, invalidate?: Invalidator<number> | undefined) => Unsubscriber;
    increment: () => void;
    decrement: () => void;
    reset: () => void;
}

Passing via <Child {count}/> has the same problem.
奥布拉兹

QUESTION: How can this be done?

Alternative

It doesn't. You can only create a store in a module, and import it - then the types are preserved.
But this creates some problems with store validity, as I wrote about at the beginning.

There are two primary options: Use preprocess-svelte and write TypeScript or add JSDoc type annotations.

With TS:

<script lang="ts">
    import { getContext } from 'svelte';

    // Prop
    export let count: StoreType;

    // Context
    const store = getContext<StoreType>('context');
</script>

With JSDoc:

<script>
    import { getContext } from 'svelte';

    // Prop
    /** @type {StoreType} */
    export let count;

    // Context
    /** @type {StoreType} */
    const store = getContext('context');
</script>

You can declare types in separate .d.ts files or use @typedef .


The createCount function can be extracted to a separate file without issue. That way its return type can also be used when typing props/contexts. Eg

// create-count.js
import { writable } from 'svelte/store';

export function createCount() {
    const { subscribe, set, update } = writable(0);

    return {
        subscribe,
        increment: () => update(n => n + 1),
        decrement: () => update(n => n - 1),
        reset: () => set(0),
    };
}
<script>
    import { getContext } from 'svelte';

    /** @type {ReturnType<import('./create-count').createCount>} */
    export let count;

    /** @type {ReturnType<import('./create-count').createCount>} */
    const store = getContext('context');
</script>

In TS this is cleaner, since you can just import the type as part of regular code and use type aliases.

<script lang="ts">
    import type { createCount } from './create-count';
    import { getContext } from 'svelte';

    export let count: Count;

    const store = getContext<Count>('context');

    type Count = ReturnType<typeof createCount>;
</script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM