TypeScript - incorrect types for provide / inject / InjectionKey
Vue version
3.3.4
Link to minimal reproduction
https://codesandbox.io/p/sandbox/nifty-dust-hvxpw7?welcome=true
See: Consumer.vue
Steps to reproduce
<template>{{ fooBar }} {{ nulls }}</template>
<script setup lang="ts">
import { inject, type InjectionKey } from "vue";
// # PROBLEM 1 - Default values are assumed as the type
// This type should be `unknown | 'fooDefault'`
// Instead the type is "fooDefault", which is not guaranteed
const foo = inject("foo", "fooDefault");
// This type should be `unknown | 'bar'`
const bar = inject("bar", "bar");
// This should throw a TypeScript error, because both values should be `unknown`
const fooBar = foo + bar;
// illustrated more clearly
const null1 = inject("null1", "foo");
const null2 = inject("null2", "bar");
// This absolutely should be a TypeScript error
const nulls = null1 + null2;
// # PROBLEM 2 - Following the documentation leads to loss of `symbol`
const sym1 = Symbol() as InjectionKey<string>;
/**
* This is a TypeScript error but _should not be_.
* @see https://github.com/microsoft/TypeScript/issues/54885
*/
let vueTestUtilsMountingOptions = {
global: {
provide: {
[sym1]: "value",
},
},
};
// Instead, Vue should be typing like:
const sym2 = Symbol() as symbol & InjectionKey<string>;
//However, typing as the above breaks `inject()` return type
// Alternatively, it could be typed like:
type Key<T> = symbol & InjectionKey<T>;
const sym3: Key<string> = Symbol();
// Works now! But provide() / inject() would still need to be fixed.
vueTestUtilsMountingOptions = {
global: {
provide: {
[sym3]: "value",
},
},
};
</script>
What is expected?
inject()
/provide()
should return correct typesInjectionKey<T>
should not override the fact that Symbol() is a symbol
What is actually happening?
inject()
and provide()
are not type-safe
System Info
System:
OS: macOS 13.4
CPU: (10) arm64 Apple M1 Pro
Memory: 765.81 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 18.13.0 - ~/Library/Caches/fnm_multishells/28527_1688831171209/bin/node
Yarn: 1.22.19 - ~/Library/Caches/fnm_multishells/28527_1688831171209/bin/yarn
npm: 8.19.3 - ~/Library/Caches/fnm_multishells/28527_1688831171209/bin/npm
Browsers:
Chrome: 114.0.5735.198
Safari: 16.5
npmPackages:
vue: ^3.3.4 => 3.3.4
Any additional comments?
I wrote a package that provides a much more type-safe pattern, is more akin to other state solutions. I think it might be worth considering something like this in Vue core? https://github.com/matthew-dean/vue-atoms