question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Describe the problem

Various types in a Kit app are app-level, by which I mean they’re the same wherever they’re used — event.locals, event.platform, session, and arguably stuff (which, even though it’s scoped to load, can be used globally as $page.stuff and is therefore probably best thought of and used as a consistently-shaped object, where input stuff and output stuff in load are both Partial<Stuff>).

It’s wasteful, therefore, that to take full advantage of type safety it’s necessary to use generic arguments everywhere:

<script context="module">
  /**
   * @type {import('@sveltejs/kit').Load<{
   *   pageParams: PageParams;
   *   session: { foo: number };
   *   stuff: Stuff;
   * }, {
   *   props: Props;
   *   stuff: Stuff
   * }>} */
  export function load({...}) {...}
</script>
/**
 * @type {import('@sveltejs/kit').RequestHandler<
 *   Locals,
 *   Platform,
 *   Output
 * >}
 */
export function get({...}) {...}
/**
 * @type {import('@sveltejs/kit').Handle<
 *   Locals,
 *   Platform
 * >}
 */
export async function handle(...) {...}

Aside: why is it pageParams rather than params?

It’s also a problem that RequestHandler and Handle take positional generic arguments — it meant that the addition of Platform was a breaking change, for example. And why doesn’t RequestHandler accept params? If we fixed that, it would be another breaking change.

With app-level types (and assuming we were to treat stuff as app-level), the examples above could be rewritten thusly:

<script context="module">
  /**
   * @type {import('@sveltejs/kit').Load<{
   *   pageParams: PageParams;
   *   props: Props;
   * }>} */
  export function load({...}) {...}
</script>
/** @type {import('@sveltejs/kit').RequestHandler<Output>} */
export function get({...}) {...}
/** @type {import('@sveltejs/kit').Handle} */
export async function handle(...) {...}

Stretch goal — no manual typing at all

A priori, it’s silly that you have to type params — SvelteKit already has that information. It should make it available… somehow.

In an ideal world, it wouldn’t be necessary to type props either — that would be inferred from the props in the <script> block.

In an even idealer world, TypeScript would support implicit module typing and you wouldn’t even have to declare types.

I’ve no idea how feasible any of this is in the real world (maybe there’s something sneaky/clever we could do with preprocess?) but this is the development experience we should be aiming for, even if we fall short.

Describe the proposed solution

I’m not much of a TypeScript expert, so I don’t really know how we would do this. @dummdidumm had a suggestion:

// src/app.d.ts
declare namespace SvelteKit {
  export interface Session { .. }
}
// inside SvelteKit
declare module "$app/stores" {
  export let session: Writable<SvelteKit.Session>
}

Alternatives considered

No response

Importance

would make my life easier

Additional Information

No response

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:4
  • Comments:12 (8 by maintainers)

github_iconTop GitHub Comments

6reactions
dummdidummcommented, Jan 30, 2022

I found a working solution which might be even more idiomatic than the namespace approach:

  1. Create the following helper type
type WithAnyFallback<T> = T extends never ? any : T;

Edit: Turns out we don’t even need this, TS falls back to any for types it does not know. 2. Add the following to for example $app/stores:

declare module '$app/stores' {
  // ..
  // @ts-ignore
  export let session: Writable<Session>; // pre-edit: Writable<WithAnyFallback<Session>>
}

Session is not defined anywhere so this is a type error (which is why we add @ts-ignore above), but thanks to the helper type it will fall back to any 3. Add the following to the user’s global.d.ts:

declare module '$app/stores' {
  export interface Session {
     whatever: 'i want';
   }
}

TypeScript will merge the module declarations, which means Session is now defined, which means intellisense will now use that type instead of any.

So the way forward would be to

  • ~define that helper type~
  • enhance global.d.ts in create-svelte with initially empty module declarations for every declaration we support to type that way with some additional comments on how to take advantage of this
3reactions
Rich-Harriscommented, Feb 1, 2022

I’d argue that the namespace is more idiomatic. It so happens that Session is used by $app/stores, but the session argument in load functions also needs to be typed, as do locals and stuff and platform which aren’t available via a store.

Separately, I think app.d.ts might be a better name than global.d.ts, since it’s the language we use elsewhere, though I don’t feel strongly on that point.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Access tokens - Slack API
The token types are suited for different functionality, and certain scopes are unique to a ... App-level tokens represent your app across organizations, ......
Read more >
App-level attribution settings - Adjust Help Center
Adjust supports two types of click-based attribution: device matching and probabilistic matching. You can turn on click-based probabilistic matching anytime ...
Read more >
App-Level Sharing - Salesforce Help
CRM Analytics apps are like folders, allowing users to organize their own data projects—both private and shared—and control sharing of dataset, lenses, an....
Read more >
App-level MFA - Okta Documentation
Configure application-level MFA to customize end-user MFA for individual applications.
Read more >
App level external content types for SharePoint 2013 - YouTube
App level external content types for SharePoint 2013. 165 views 8 years ago. Hybrid Cloud Advisor.com. Hybrid Cloud Advisor.com. 1.08K subscribers.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found