Reputation: 491
So it looks like I have to separately type the response body going out of an endpoint and the props that I get from that body in the component. For example:
// 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>
I guess my question is why am I typing the response going out if I don't benefit from that typing in the component props?
Upvotes: 6
Views: 2884
Reputation: 145
As brunnerh notes, there isn't currently (as of writing this) a good way to take the strongly-typed component/endpoint, and pass its definition to the other. In the past, I've used the endpoint to store a "response" interface, which can easily be imported by co-located components.
// +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}
I generally avoid fetching inside components, as it is usually messy.
In most cases, you can get away with data loading for GET, and form actions for POST. load()
is strongly typed, thanks to codegen.
Once inside your component, you can achieve strong-typing by either:
<script lang="ts">
import type { load } from './+page';
export let data: Awaited<ReturnType<typeof load>>;
//^ let data: { type: "success" | "error"; msg: string; }
</script>
You can return custom JSON in form actions, but it's not currently strongly typed. You have the option to combine data loading and form actions to achieve this, by migrating your API "response" logic into
load()
:form submitted -> server action ->
invalidateAll()
->load()
re-ran -> page updated
Upvotes: 0
Reputation: 185225
This simply seems unsupported at the moment. The only thing that is generated are the params
along the path (placeholders like [id]
in file names).
Also, the source of truth would be the component, not the other way around, as all endpoints besides get
can just return whatever they want.
I tried to work around this using type inference, but the generated component types do not appear to be accessible in TS files. If someone knows a workaround for that, it would be easy to type the endpoint correctly.
// 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 () => {
//...
}
Inside a Svelte file:
<script lang="ts">
import type { ComponentProps } from '$lib/typing';
import type Index from './index.svelte';
type Props = ComponentProps<Index>;
</script>
Upvotes: 3