svelte icon indicating copy to clipboard operation
svelte copied to clipboard

The getContext function has incorrect type signature

Open SoundAsleep192 opened this issue 3 years ago • 3 comments

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

SoundAsleep192 avatar Jul 25 '22 15:07 SoundAsleep192

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.

dummdidumm avatar Jul 25 '22 15:07 dummdidumm

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!

SoundAsleep192 avatar Jul 25 '22 16:07 SoundAsleep192

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');

brunnerh avatar Aug 30 '22 18:08 brunnerh

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({...});

Rich-Harris avatar Apr 02 '24 21:04 Rich-Harris