The getContext function has incorrect type signature
Describe the bug
The getContext function returns undefined if no parent component provides a context. You can't guarantee for a component to have a context without checking with hasContext in prior. Thus, by default the return type is expected to include undefined
Expected result:
declare function getContext<T>(key: any): T | undefined
Actual result:
declare function getContext<T>(key: any): T
Reproduction
https://svelte.dev/repl/718b6e5da6e64a06be7165a86965cfeb?version=3.49.0
Logs
No response
System Info
System:
OS: macOS 12.4
CPU: (8) arm64 Apple M1 Pro
Memory: 60.47 MB / 16.00 GB
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 16.14.1 - ~/Library/Caches/fnm_multishells/61915_1657654538285/bin/node
Yarn: 1.22.17 - ~/Library/Caches/fnm_multishells/61915_1657654538285/bin/yarn
npm: 8.5.0 - ~/Library/Caches/fnm_multishells/61915_1657654538285/bin/npm
Browsers:
Chrome: 103.0.5060.134
Safari: 15.5
Severity
annoyance
I'm split. On one hand - yes, things can be undefined when they don't exist -, on the other hand, if you type this function you probably already made sure that this in fact exists in that part of the code base, so changing this would mean worse ergonomics. Either way, it's a breaking change for everyone who uses strict null checks, so we can do this in Svelte 4 at the earliest.
if you type this function you probably already made sure that this in fact exists in that part of the code base
Sounds reasonable! And in most cases it should be like that indeed, unless you write something pretty generic which can be also used outside the context.
For example, in a project I'm currently working on we decided to create component-adapters for <form> and <input> elements. And of course you can use inputs outside a form!
I would suggest keeping it as is due to the mentioned ergonomics.
If a component can validly be used without the given context, the undefined can still be added as part of T. E.g.
const x = getContext<number | undefined>('key');
Looks like we're settled on this question, so I'll close this. FWIW when I want type safety with context I'll generally have a utility module that does something like this...
import { getContext, setContext } from 'svelte';
const key = {};
interface Context {
// my context definition
}
export function get() {
return getContext(key) as Context;
}
export function set(context: Context) {
return setContext(key, context);
}
...and use it like this:
import * as context from '$lib/context';
context.set({...});