Allow locals to be typed specifically for subpages of layouts
Describe the problem
When you have a route like this: /game/{gameId} all subpages of this route will want to access this game.
Normally what you do is create a +layout.server.ts and load and return the game in the load function:
// +layout.server.js
export const load = ({ params }) => {
const game = loadGame(params)
return { game }
}
// +page.server.js
export const load = async ({ parent }) => {
const game = await parent()
return { state: game.state }
}
This works fine, however sometimes the object that you need to load can’t (or shouldn’t) be serialized and remain on the server.
For this you need to put it in the locals object. But the locals object cannot be typed for specific routes.
So in every subpage of /game/{gameId} you need this logic:
// +page.server.js
export const load = async ({ locals }) => {
const game = locals.game
if (!game) throw error(500, ‘Game was not available’);
return { state: game.state }
}
Describe the proposed solution
Provide a way to type locals for subroutes. I’m not sure how this could be done best, maybe the +layout.server.ts file could export a type like this:
export type Locals = {
game: Game
}
The sveltekit compiler would need to take this into consideration when generating the type definitions.
Alternatives considered
Just globally modify the locals type in app.d.ts and add the optional type there:
declare global {
namespace App {
// interface Error {}
interface Locals {
/** This is set in all subroutes of /game/{gameId} */
game?: Game
}
// interface PageData {}
// interface Platform {}
}
}
Importance
nice to have
Additional Information
No response
This would be gold! I also ran into this today and played around with ambient.d.ts files to overwrite the PageServerLoad type...without success though. Things just got worse. :smile:
Maybe the same could also be done for PageState? With an app growing it's a bit painful to always track a huge PageState object.
Currently I do
interface PageState {
details: User | Folder | Bla | Foo | null }
}
So whenever I use it, for example in profile.svelte, i do this:
{#if isUser($page.state.details)}
<UserDetails user={$page.state.details} />
{/if}
and when I use it on a file navigation view:
{#if isFolder($page.state.details)}
<FolderDetails user={$page.state.details} />
{/if}
This is quite boilerplaty. It would be so much nicer to have $page.state.details simply be User if the route is a child of /(profile)/ and $page.state.details should be of type Folder if it's below /(filebrowser)/.
Edit: I added this here in the locals discussion since I think this might have the same root: we have one global type for potentially a lot of different views.