feat: add `createContext` utility for type-safe context (#16948)

* feat: add `createContext` utility for type-safe context

* regenerate
pull/16946/head
Rich Harris 2 days ago committed by GitHub
parent 99711d5822
commit f3c55e8e6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': minor
---
feat: add `createContext` utility for type-safe context

@ -60,6 +60,14 @@ Certain lifecycle methods can only be used during component initialisation. To f
<button onclick={handleClick}>click me</button>
```
### missing_context
```
Context was not set in a parent component
```
The [`createContext()`](svelte#createContext) utility returns a `[get, set]` pair of functions. `get` will throw an error if `set` was not used to set the context in a parent component.
### snippet_without_render_tag
```

@ -52,6 +52,12 @@ Certain lifecycle methods can only be used during component initialisation. To f
<button onclick={handleClick}>click me</button>
```
## missing_context
> Context was not set in a parent component
The [`createContext()`](svelte#createContext) utility returns a `[get, set]` pair of functions. `get` will throw an error if `set` was not used to set the context in a parent component.
## snippet_without_render_tag
> Attempted to render a snippet without a `{@render}` block. This would cause the snippet code to be stringified instead of its content being rendered to the DOM. To fix this, change `{snippet}` to `{@render snippet()}`.

@ -242,7 +242,13 @@ function init_update_callbacks(context) {
}
export { flushSync } from './internal/client/reactivity/batch.js';
export { getContext, getAllContexts, hasContext, setContext } from './internal/client/context.js';
export {
createContext,
getContext,
getAllContexts,
hasContext,
setContext
} from './internal/client/context.js';
export { hydrate, mount, unmount } from './internal/client/render.js';
export { tick, untrack, settled } from './internal/client/runtime.js';
export { createRawSnippet } from './internal/client/dom/blocks/snippet.js';

@ -39,6 +39,12 @@ export async function settled() {}
export { getAbortSignal } from './internal/server/abort-signal.js';
export { getAllContexts, getContext, hasContext, setContext } from './internal/server/context.js';
export {
createContext,
getAllContexts,
getContext,
hasContext,
setContext
} from './internal/server/context.js';
export { createRawSnippet } from './internal/server/blocks/snippet.js';

@ -69,6 +69,26 @@ export function set_dev_current_component_function(fn) {
dev_current_component_function = fn;
}
/**
* Returns a `[get, set]` pair of functions for working with context in a type-safe way.
* @template T
* @returns {[() => T, (context: T) => T]}
*/
export function createContext() {
const key = {};
return [
() => {
if (!hasContext(key)) {
e.missing_context();
}
return getContext(key);
},
(context) => setContext(key, context)
];
}
/**
* Retrieves the context that belongs to the closest parent component with the specified `key`.
* Must be called during component initialisation.

@ -10,6 +10,15 @@ export function set_ssr_context(v) {
ssr_context = v;
}
/**
* @template T
* @returns {[() => T, (context: T) => T]}
*/
export function createContext() {
const key = {};
return [() => getContext(key), (context) => setContext(key, context)];
}
/**
* @template T
* @param {any} key

@ -51,6 +51,22 @@ export function lifecycle_outside_component(name) {
}
}
/**
* Context was not set in a parent component
* @returns {never}
*/
export function missing_context() {
if (DEV) {
const error = new Error(`missing_context\nContext was not set in a parent component\nhttps://svelte.dev/e/missing_context`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/missing_context`);
}
}
/**
* Attempted to render a snippet without a `{@render}` block. This would cause the snippet code to be stringified instead of its content being rendered to the DOM. To fix this, change `{snippet}` to `{@render snippet()}`.
* @returns {never}

@ -0,0 +1,7 @@
<script>
import { get } from './main.svelte';
const message = get();
</script>
<h1>{message}</h1>

@ -0,0 +1,5 @@
import { test } from '../../test';
export default test({
html: `<h1>hello</h1>`
});

@ -0,0 +1,16 @@
<script module>
import { createContext } from 'svelte';
/** @type {ReturnType<typeof createContext<string>>} */
const [get, set] = createContext();
export { get };
</script>
<script>
import Child from './Child.svelte';
set('hello');
</script>
<Child />

@ -448,6 +448,10 @@ declare module 'svelte' {
}): Snippet<Params>;
/** Anything except a function */
type NotFunction<T> = T extends Function ? never : T;
/**
* Returns a `[get, set]` pair of functions for working with context in a type-safe way.
* */
export function createContext<T>(): [() => T, (context: T) => T];
/**
* Retrieves the context that belongs to the closest parent component with the specified `key`.
* Must be called during component initialisation.

Loading…
Cancel
Save