typescript - Hur man starkt typar svaret från en SvelteKit-endpoint i komponenten?


Så det ser ut som att jag måste skriva ut svarskroppen som går ut från en endpoint och de egenskaper som jag får från den kroppen i komponenten separat. Till exempel:

// index.ts (endpoint)

import type { RequestHandler } from "./__types";

export const get: RequestHandler<{
  foo: number;
  bar: string;
}> = () => {
  return {
    body: {
      foo: 42,
      bar: "hello"
    }
  };
};
// index.svelte (component)

<script lang="ts">
  export let foo;  // Not typed?
  export let bar;
</script>

Jag antar att min fråga är varför jag skriver svaret som går ut om jag inte drar nytta av att skriva in det i komponentens egenskaper?

Svar #1

Detta verkar helt enkelt inte vara stöd för för tillfället. Det enda som genereras är params längs vägen (platshållare som [id] i filnamn).

Dessutom skulle sanningskällan vara komponenten, inte tvärtom, eftersom alla slutpunkter förutom get bara kan returnera vad de vill.


Jag försökte hitta en lösning på detta med hjälp av typpinferens, men de genererade komponenttyperna verkar inte vara tillgängliga i TS-filer. Om någon känner till en lösning på det skulle det vara enkelt att korrekt typa slutpunkten.

// src/lib/typing.ts
import type { SvelteComponentTyped } from 'svelte';

export type ComponentProps<T> = T extends SvelteComponentTyped<infer P> ? P : never;
import type { RequestHandler } from './__types';
import type Index from './index.svelte';
import type { ComponentProps } from '$lib/typing';

type Props = ComponentProps<Index>; // Unfortunately resolved to `any`

export const get: RequestHandler<Props> = async () => {
  //...
}

Inuti en Svelte-fil:

<script lang="ts">
    import type { ComponentProps } from '$lib/typing';
    import type Index from './index.svelte';

    type Props = ComponentProps<Index>;
</script>

Svar #2

Som brunnerh noterar, finns det för närvarande (vid skrivande stund) inget bra sätt att ta den starkt typade komponenten/ändpunkten och överföra dess definition till den andra. Tidigare har jag använt ändpunkten för att lagra ett "svar"-gränssnitt, som enkelt kan importeras av komponenter som befinner sig på samma plats.

// +server.ts
import { json } from '@sveltejs/kit';

export const GET = async () => {
    // ...
    return json({ type: "success", msg: "Hello, world!" } satisfies _res);
                                                       // ^ typed
};

export type _res = {
    type: "success" | "error";
    msg: string;
};
<!-- Component.svelte -->
<script lang="ts">
    import { onMount } from 'svelte';
    import type { _res } from './endpoint/+server';

    let text = '';
    onMount(async () => {
        const res = await fetch('/version/endpoint');
        const obj: _res = await res.json();
                 //^ typed
        text = obj.msg;
    });
</script>

{text}

Anteckning om modern SvelteKit

Jag undviker vanligtvis att hämta inuti komponenter, eftersom det oftast blir rörigt.

I de flesta fall kan du klara dig med dataladdning för GET och formåtgärder för POST. load() är starkt typad, tack vare codegen.

Väl inne i din komponent kan du uppnå stark typning genom att antingen:

  • skapa ett gränssnitt (följ exemplet ovan)
  • importera typen från sidfunktionen:
<script lang="ts">
    import type { load } from './+page';
    export let data: Awaited<ReturnType<typeof load>>;
             //^ let data: { type: "success" | "error"; msg: string; }
</script>

Du kan returnera anpassad JSON i formåtgärder, men det är för närvarande inte starkt typat. Du har möjlighet att kombinera dataladdning och formåtgärder för att uppnå detta, genom att migrera din API "response"-logik till load():

formulär skickat -> serveråtgärd -> invalidateAll() -> load() kördes om -> sidan uppdaterad

Relaterade artiklar