Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use ContextKey<T> to type setContext, getContext and hasContext #8941

Open
olehmisar opened this issue Jul 8, 2023 · 6 comments · May be fixed by #11042
Open

Use ContextKey<T> to type setContext, getContext and hasContext #8941

olehmisar opened this issue Jul 8, 2023 · 6 comments · May be fixed by #11042

Comments

@olehmisar
Copy link

olehmisar commented Jul 8, 2023

Describe the problem

Currently getContext returns unknown if no explicit type is provided.
image

Describe the proposed solution

Taking inspiration from svelte-typed-context, getContext can accept an InjectionKey<T>ContextKey<T> and return T:
image

This will improve type safety and can be done in a backwards compatible way.

The implementation would be just to add a new signature to getContext, setContext and hasContext

export interface ContextKey<T = unknown> { }
export function setContext<T>(key: ContextKey<T>, context: T): void
// getContext ...
// hasContext ...
// check for full implementation here:
// https://github.com/KamenKolev/svelte-typed-context/blob/master/index.ts
// (published npm package does not include `hasContext`, so install only from github)

Alternatives considered

Importance

nice to have

Search terms

typed context inject provide

@brunnerh
Copy link
Member

Maybe just call it ContextKey, though?

@olehmisar
Copy link
Author

olehmisar commented Nov 17, 2023

@dummdidumm i just checked that adding a new function signature to {set,get,has}Context while preserving backwards compatibility is impossible. It's because the key argument is of type unknown and the new signature will not be typesafe because of that. If this feature is planned for the future, I think it's a great candidate for a new major release because it will be a breaking change.

@olehmisar olehmisar changed the title Use injection key to type setContext and getContext Use ContextKey<T> to type setContext, getContext and hasContext Nov 17, 2023
@Rich-Harris Rich-Harris added this to the 5.0 milestone Jan 29, 2024
@oscarhermoso
Copy link

You can add below snippet to src/app.d.ts and override the existing types 🙂

type Context = {
	theme: Writable<string | null>; // for example
};

declare module 'svelte' {
	export function getContext<T>(key: T extends keyof Context ? T : never): Context[T];

	export function setContext<T>(
		key: T extends keyof Context ? T : never,
		context: Context[T],
	): void;
}

@NickClark
Copy link

To extend the ideas mentioned above, my preferred app.d.ts flavor is:

declare module "svelte" {
  export interface ContextKey<T = unknown> {} // eslint-disable-line @typescript-eslint/no-unused-vars

  export function getContext<T>(key: ContextKey<T>): T;
  export function setContext<T>(key: ContextKey<T>, value: T): void;
  export function hasContext<T>(key: ContextKey<T>): boolean;
}

Then you can define your key as simply as:

export const TabContext: ContextKey<Tab[]> = {};

@NickClark
Copy link

The only real issue with the solution above, is that setContext ends up being an overload, so when trying to set the context value it falls back to the generic definition and won't autocomplete.

setContext(MyContextKey, { /* Typescript won't autocomplete this */ });

Anyone know of a way to override instead of extend?

dummdidumm added a commit that referenced this issue Apr 3, 2024
…text`

Not completly ideal because you can circumvent the type safety by doing `getContext<SomeType>(context_key)` - changing this would require a breaking change, which we could do in Svelte 6 after we've given `ContextKey` some time to establish itself.
Also doesn't add the interesting type narrowing idea in https://github.com/KamenKolev/svelte-typed-context/blob/master/index.ts#L14 (yet), probably easier to do together with said breaking change.

closes #8941
@Carlos-err406
Copy link

Carlos-err406 commented Apr 9, 2024

i also tried the approaches above but cant get autocompletion on the value of set context, only on the key
here is my own flavor, importing Context from $lib/types

declare module 'svelte' {
	export function getContext<T extends keyof Context, K extends Context[T]>(key: T): K;
	export function setContext<T extends keyof Context, K extends Context[T]>(
		key: T,
		context: K
	): void;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants