You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Go to definition for TypeScript literal strings #49033
Given the below code, I would really like to be able to jump to the definition of the 'foo':
constobj={foo: 'bar'asconst,hello: 'world'asconst,}functionget<T,KextendskeyofT>(obj: T,path: K): T[K]{returnobj[path]}constval=get(obj,'foo')// 👆👆👆 Is there a way of make jump to definition work here?// I really want to CMD+click `'foo'` and jump to line #2
Cc Ryan Cavanaugh (@RyanCavanaugh) is there a way to put bug bounties on issues in order to incentivize OSS-contributors to take on tasks ahead of the core team? I'd happily put forward a few hundred $ personally for this to be solved as it's a big pain point in my API design of tRPC
Interesting question; we do not currently have a mechanism for that. Other projects have had people put up bounties on third-party sites, e.g. dotnet/runtime#36748 (comment) . I was actually just on an internal email thread about this and the general consensus was that we're OK with good-faith bounties being staked, but want to "stay neutral" in terms of e.g. deciding which of two contributors should be awarded a bounty if both put up a PR.
Reacted by Alex / KATT, Theo Browne and Jonas Strassel
Reacted by Alex / KATT, Theo Browne, Jonas Strassel and Alexey Berezin
Reacted by Alex / KATT, Jonas Strassel, Josh Ghoulberg 👻, bautistaaa, Sachin Raja, Alexey Berezin, Oscar Beaumont, Alistair Smith, Kishan Mandora, Khang Hoang and 6 more
Reacted by Alex / KATT, Jonas Strassel, Josh Ghoulberg 👻, bautistaaa, Sachin Raja, Jakob Guddas, Lenz Weber-Tronic, August “Kai” Kaiser, Alistair Smith, Kishan Mandora and 6 more
Reacted by Butter fly, Khang Hoang, Oscar Beaumont, Audun Meek Olsen and ColemanDunn
Mateusz Burzyński (@Andarist) and I chatted a bit about this one. We suspect it's not straightforward or trivial to implement, but hopefully doable in our time budgets.
We'd both separately done similar investigations. Take my rough notes with a grain of salt because I'm not on the TypeScript team and don't know anything for sure:
The Go to Definition codefix comes from goToDefinition.ts
Eventually the call stack goes to function getSymbol -> checker.getSymbolAtLocation
In here, the case SyntaxKind.StringLiteral case falls through to SyntaxKind.NumericLiteral
Nothing within those two cases gets hit, so it returns undefined
Backing context:
Literals don't have rich (per-node) symbol information. There's one global symbol per literal type.
Individual nodes seem to store a grab-bag of information in nodeLinks, accessed via getNodeLinks.
Potential Implementation Directions
We discussed these three, in increasing order of likelihood to succeed:
Create new symbols for the literals
See what type the parent location is
Node links / getNodeLinks
Create new symbols for the literals
Is it possible that we could have more rich symbols per literal? Maybe yes, but that'd be a pretty big breaking change in how TypeScript and its consumers understand symbols. I'm under the impression many consumers -such as typescript-eslint- assume one symbol-per-literal, not one-symbol-per-literal-and-type. 👎
See what type the parent location is
Is there a compiler API for "given a node, tell me what type it's expected to be"? E.g. if you're looking at a function argument, determining the intended type of the parameter? Neither of us could find one.
If one exists: yes please, that would be an ideal solution!
If not: ah well.
Augment node links
NodeLinks seems to already be a catch-all place to put information on nodes. Can we add something like a resolvedLiteralType?: Type property for literals?
getNodeLinks is called all over the place. Neither of us have investigated where adding this information for literals would be possible, if anywhere.
Again: don't take these notes too seriously; we might be totally off in our direction. But either way: progress! 🚀
Here, a has the literal type "hello". It went through intersection reduction via an identity match between "hello" from keyof typeof obj1 and keyof typeof obj2. You can't create new literal types for those keys depending on where they came from; this will have totally uncool performance characteristics (allocations, forcing intersection to do more work, and GC pressure). So you're necessarily left with just the bare literal type "hello" with no real information of where it came from.
So this is interesting. Yes, checker.getContextualType does retrieve the union type - thank you!
But the union type doesn't have a .symbol referring back to the keyof typeof.
In fact, literal values and operator-computed values such as keyof typeof don't have type definitions.
Trying to Go to type definition on the following variables' names...
const valA = {};: highlights the {} ✅
const valB = true;: gives No type definition found for 'valB' ❌
const valC = typeof valA;: gives No type definition found for 'valC' ❌
Is it intentional that the type doesn't have a symbol? Could we add one in as part of this change?
declare let a: (keyof typeof obj1) & (keyof typeof obj2);
I would think the Go to definition would be on the type annotation in full: (keyof typeof obj1) & (keyof typeof obj2). That would be the expected checker.getContextualType behavior, no?
I would think the Go to definition would be on the type annotation in full: (keyof typeof obj1) & (keyof typeof obj2). That would be the expected checker.getContextType behavior, no?
That matches my thinking too.
As to getContextualType - I was trying to play around with it before but I couldn't make any use of it. With the given simple get example if I call typeChecker.getContextualType(node) I just end up with the literal type itself. Should it be called on another object?
Reacted by Josh Ghoulberg 👻, Oliver Joseph Ash, Maxime Richard, Herrington Darkholme, div, August “Kai” Kaiser, Joe Previte, Joker_, Alistair Smith, Arad Alvand and 3 more
Any updates on this, would really love to have this behavior! The use case I'm most familiar with is when I create function names using string literal types:
its very annoying that we're not able to go to definition on these generated functions from anywhere in the code
Activity
KATT commentedon May 13, 2022
Cc Ryan Cavanaugh (@RyanCavanaugh) is there a way to put bug bounties on issues in order to incentivize OSS-contributors to take on tasks ahead of the core team? I'd happily put forward a few hundred $ personally for this to be solved as it's a big pain point in my API design of tRPC
RyanCavanaugh commentedon May 13, 2022
Interesting question; we do not currently have a mechanism for that. Other projects have had people put up bounties on third-party sites, e.g. dotnet/runtime#36748 (comment) . I was actually just on an internal email thread about this and the general consensus was that we're OK with good-faith bounties being staked, but want to "stay neutral" in terms of e.g. deciding which of two contributors should be awarded a bounty if both put up a PR.
KATT commentedon May 16, 2022
t3dotgg commentedon Jun 20, 2022
I'll throw in another $500 on this bounty Alex / KATT (@KATT)
JoshuaKGoldberg commentedon Jun 28, 2022
Mateusz Burzyński (@Andarist) and I chatted a bit about this one. We suspect it's not straightforward or trivial to implement, but hopefully doable in our time budgets.
Edit: Ryan Cavanaugh (@RyanCavanaugh) responded immediately after this long text with answer (
checker.getContextualType)Prior Debugging Notes
We'd both separately done similar investigations. Take my rough notes with a grain of salt because I'm not on the TypeScript team and don't know anything for sure:
goToDefinition.tsfunction getSymbol->checker.getSymbolAtLocationcase SyntaxKind.StringLiteralcase falls through toSyntaxKind.NumericLiteralundefinedBacking context:
nodeLinks, accessed viagetNodeLinks.Potential Implementation Directions
We discussed these three, in increasing order of likelihood to succeed:
Create new symbols for the literals
Is it possible that we could have more rich symbols per literal? Maybe yes, but that'd be a pretty big breaking change in how TypeScript and its consumers understand symbols. I'm under the impression many consumers -such as typescript-eslint- assume one symbol-per-literal, not one-symbol-per-literal-and-type. 👎
See what type the parent location is
Is there a compiler API for "given a node, tell me what type it's expected to be"? E.g. if you're looking at a function argument, determining the intended type of the parameter? Neither of us could find one.
If one exists: yes please, that would be an ideal solution!
If not: ah well.
Augment node links
NodeLinksseems to already be a catch-all place to put information on nodes. Can we add something like aresolvedLiteralType?: Typeproperty for literals?getNodeLinksis called all over the place. Neither of us have investigated where adding this information for literals would be possible, if anywhere.Again: don't take these notes too seriously; we might be totally off in our direction. But either way: progress! 🚀
RyanCavanaugh commentedon Jun 28, 2022
This is
getContextualTypeRyanCavanaugh commentedon Jun 28, 2022
I'm not even sure how this can be made to work in practice. Let's consider something like
Here,
ahas the literal type"hello". It went through intersection reduction via an identity match between"hello"fromkeyof typeof obj1andkeyof typeof obj2. You can't create new literal types for those keys depending on where they came from; this will have totally uncool performance characteristics (allocations, forcing intersection to do more work, and GC pressure). So you're necessarily left with just the bare literal type"hello"with no real information of where it came from.JoshuaKGoldberg commentedon Jun 29, 2022
So this is interesting. Yes,
checker.getContextualTypedoes retrieve the union type - thank you!But the union type doesn't have a
.symbolreferring back to the keyof typeof.In fact, literal values and operator-computed values such as
keyof typeofdon't have type definitions.Trying to Go to type definition on the following variables' names...
const valA = {};: highlights the{}✅const valB = true;: gives No type definition found for 'valB' ❌const valC = typeof valA;: gives No type definition found for 'valC' ❌Is it intentional that the type doesn't have a symbol? Could we add one in as part of this change?
I would think the Go to definition would be on the type annotation in full:
(keyof typeof obj1) & (keyof typeof obj2). That would be the expectedchecker.getContextualTypebehavior, no?Andarist commentedon Jun 29, 2022
That matches my thinking too.
As to
getContextualType- I was trying to play around with it before but I couldn't make any use of it. With the given simplegetexample if I calltypeChecker.getContextualType(node)I just end up with the literal type itself. Should it be called on another object?RyanCavanaugh commentedon Jun 29, 2022
Yes. Literal types and unions don't have symbols because there's not an originating declaration for them; both are interned.
JoshuaKGoldberg commentedon Jun 29, 2022
Sorry to dig, but what does "interned" mean here?
RyanCavanaugh commentedon Jun 29, 2022
https://en.wikipedia.org/wiki/Interning_(computer_science)
RyanCavanaugh commentedon Jun 29, 2022
GitHub has changed Ctrl-Enter to "Close" and I am furious 👿
Edit: Oh thank god https://twitter.com/mariorod1/status/1542189296240713730
sghsri commentedon Dec 21, 2022
Any updates on this, would really love to have this behavior! The use case I'm most familiar with is when I create function names using string literal types:
its very annoying that we're not able to go to definition on these generated functions from anywhere in the code
feat: support some most-basic cases from microsoft/TypeScript#49033 (…