typescript - Hur sätter jag typen för en anpassad store när jag skickar store via setContext eller komponentprop?
När du använder SvelteKit måste store alltid skapas i routen page/layout, annars uppstår problem med att statusen är uppdaterad efter uppdatering av sidan.
Du kan inte skapa ett store i en importerad modul, eftersom server-sidans modul bara laddas en gång, och detta orsakar fel i store.
Allt detta leder till en viss situation med typer.
Problemet:
Jag skapar och skickar butiken från +page.svelte till 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>
I Child.svelte är typen för store count any:
Och det ska vara som på platsen för deklarationen:
Det är vad det borde vara typen av store, som returneras från getContext("store"):
const count: {
subscribe: (this: void, run: Subscriber<number>, invalidate?: Invalidator<number> | undefined) => Unsubscriber;
increment: () => void;
decrement: () => void;
reset: () => void;
}
Att skicka via <Child {count}/> har samma problem.
FRÅGA: Hur kan detta göras?
Alternativ
Det gör det inte. Du kan bara skapa en store i en modul och importera den - då bevaras typerna.
Men detta skapar vissa problem med store-giltighet, som jag skrev om i början.
Svar #1
Det finns två huvudsakliga alternativ: Använd preprocess-svelte och skriv TypeScript eller lägg till JSDoc-typannoteringar.
Med TS:
<script lang="ts">
import { getContext } from 'svelte';
// Prop
export let count: StoreType;
// Context
const store = getContext<StoreType>('context');
</script>
Med JSDoc:
<script>
import { getContext } from 'svelte';
// Prop
/** @type {StoreType} */
export let count;
// Context
/** @type {StoreType} */
const store = getContext('context');
</script>
Du kan deklarera typer i separata .d.ts-filer eller använda @typedef.
Funktionen createCount kan extraheras till en separat fil utan problem. På så sätt kan dess returtyp också användas vid typning av props/context. T.ex.
// 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>
I TS är detta renare, eftersom du bara kan importera typen som en del av vanlig kod och använda typalias.
<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>


