Repository navigation
Interfaces that extend 'Symbol' cannot be used as 'symbol' type directly #46956
Description
Activity
A wrapper object type(
Symbol, Number, String...) is not assignable to the corresponsive primitive type(symbol, number, string...). And the doc says you should avoid using them in practice.In your example, consider making the
InjectionKeyas a intersection type.type InjectionKey<T> = symbol & T;
yusufkandemir commented
on Nov 30, 2021 AuthorMore actionswhzx5byb thanks for the info and suggestions 👏.
type InjectionKey<T> = symbol & T;
wouldn't be the same as
interface InjectionKey<T> extends Symbol {}
Tshouldn't be a possible value.So, it would be like
type InjectionKey<T> = symbol;
then the type becomes primitive, which makes extracting the generic parameter type impossible.
I also found that the docs don't recommend using a generic type parameter with an unused type parameter, see here. So, this means the code I shared is wrong in many ways. But, I think it's the only way possible to create symbols that are paired with a specific type.
See this playground for a use case, with two approaches.
At this point, it looks like it's against the best practices, but the only way to solve a particular problem, at least the people at Vue, and also me after digging through this. So, I guess the maintainers could just close the issue because it's against their recommendation(which is fine), provide a legit alternative solution to this problem(it would be fantastic), or search for ways to create a legit way if it doesn't exist yet(i.e. they are willing to accept a feature request about this).
- addedWorking as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
on Nov 30, 2021 RyanCavanaugh commented
on Nov 30, 2021 MemberMore actionsYeah, this code just isn't doing what you think it's doing. The type
Symbolis an object type, and object types aren't valid object keys (the same reasonfoo[{ }]is not allowed). The same problem is present withnumber/Number, etctypescript-bot commented
on Dec 3, 2021 ContributorMore actionsThis issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.
Yusuf Kandemir (@yusufkandemir) the
InjectionKeyonvue 3is to be used only with provide/injectThe reason that
extends Symbolis to make sure the user is using with aunique symbolto guarantee the dependency at runtime.If someone is using
InjectionKey<T>to direct access a property (like the example on the issue) that's is not supported byvue.Since this interface is only to be used with
provide/injectinvueI don't think there's an issue with the current implementation onvue 3, since the goal is to have a typed safe interface forinject/provide.yusufkandemir commented
on Dec 8, 2021 AuthorMore actionsCarlos Rodrigues (@pikax) I appreciate the explanation. The main idea behind this was basically to have an
appInjecthelper, which is still possible to achieve, and not directly related to this issue. But, when playing around with the idea, I came across some problems and dived into how everything works, how TS behaves, etc., and decided to open this issue because it seemed like a bug.I guess you are mainly referring to this:
So, this means the code I shared is wrong in many ways.
which is correct in terms of "being wrong according to the TS best practices".
And, it is followed byBut, I think it's the only way possible to create symbols that are paired with a specific type.
and similar supporting sentences.
So, with the last answer I received from Ryan Cavanaugh(don't want to bother with a ping), the situation became clearer(I hope I understood it correctly 😛). The current implementation in Vue 3 is correct, because it just works and it's clean to use and understand, and there is no other way possible to achieve this, even though it looks like it's against TS best practices. But, I think it's safe to say that if this is the intended behavior of TS, and there is no other way of achieving such a thing (both of which seems to be the case), we can't really say the current implementation on Vue 3 is against the TS best practices. A best practice becomes invalid when it completely blocks us from achieving something. By the way, I love your work ❤️
Since
vuealso need to allow definingprovide/injectthrough the object API, is worth to investigate a bit further, based on the whzx5byb it works but adds the possibility of assigningT as InjectionKey<T>which is not correct.Based on that I think is save to do something like:
declare const InjectionSymbol: unique symbol type InjectionKey<T> = symbol & { [InjectionSymbol]: T }; //@ts-expect-error it cannot be the same type const foo = {foo: false} as InjectionKey<{foo: false}>
The only issue, it works now but will it work in the future or will typescript support this type in the future? For example
PropertyKey<T>typed?- locked as resolved and limited conversation to collaborators
on Oct 21, 2025
Bug Report
🔎 Search Terms
extends Symbol
extends Symbol index key
extending Symbol
InjectionKey
cannot be used as an index type
🕗 Version & Regression Information
⏯ Playground Link
Playground link with relevant code
💻 Code
🙁 Actual behavior
Interfaces that extend
Symbol(e.g.InjectionKey) can't be used assymboldirectly although they can be explicitly cast.🙂 Expected behavior
Interfaces that extend
Symbolcan be used assymbol, which includes being used as index types.