-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose TypeChecker.getTypeOnlyAliasDeclaration
or similar API to detect if a symbol is a "value" or a "type"
#57032
Comments
FWIW I don’t know if the above affects the feature request here but I thought it was worth pointing out. |
Thanks for bringing this, but this is exactly what I need to differentiate. I'm building a bundler of dts files and the result of dts file should represent exactly what you can find in the js file. If a developer added exporting the type of a class instead of the class itself in their ts file, the dts bundle (the same as generated dts file by the compiler has) should have exporting the type, not exporting the class. At the moment I don't see how it is possible to tell whether a symbol that represents a module export is "type only" or "value". |
I am not mega familiar with this API; @andrewbranch do you know if this is something that is reasonable to export? It does "do the job", but its API is a little funky for knowing if a particular alias is type only. But, I suppose getting the declaration may be useful in general. It was added back in #45998. (At some level, simply exposing it would be nice given it's been there since the beginning of type imports and therefore may be useful with older TS versions, kinda like |
I am really afraid of exposing that API, but we need to have some solution for declaration bundlers and transformers. @timocov does it work to instead do something like while (aliasSymbol?.flags! & ts.SymbolFlags.Alias) {
const typeOnly = aliasSymbol.declarations.find(ts.isTypeOnlyImportOrExportDeclaration);
if (typeOnly) return typeOnly;
aliasSymbol = checker.getImmediateAliasedSymbol(aliasSymbol);
} ? Note: when testing this approach, ensure you have a test with |
For be frank I don't need this API directly, if it would be something that would return a boolean when a symbol is "type" or "value" it would be more than enough (at least for my use-case). I tried to use
Thanks @andrewbranch, that's good to know. I recently tried to address I didn't have such test before, but apparently your approach above isn't working for it (I just added it and there is a diff with |
I'm ready to use any "workaround" or approach the compiler team suggests, but it feels like that this information cannot be easily extracted from AST (or mostly relying on AST but use symbols as a helper) without handling lots of edge-cases, but also it feels like this information "belongs" to type checker (or a symbol). If my assumption about |
Do you happen to have a piece of code where |
The one thing I'm seeing is that in But, it does make me think that maybe some element of that is the export? Not sure... |
@jakebailey that's exactly index.ts: import { AnotherFoobar } from './re-export-type';
export {
AnotherFoobar,
}; re-export-type.ts: export type * from './another-class'; another-class.ts: export class AnotherFoobar {} In this example I'm operating with the exports of I can push a branch with a test case I use and instructions how to run/debug it (it not complicated I promise 😂) if that helps.
Oh it looks like I followed the correct path initially 😄 |
A branch would be helpful; mainly I was looking for the test cases and things in your repo that I could plug various different ideas into. But, if it's just the above, I can try just that. |
It should be just it, but in case if it would be helpful:
For this particular issue I added 2 test cases |
I think your idea of what a type-only import or export is is still off and it’s leading you a bit astray here. A type-only alias doesn’t change anything about its target symbol. Even the alias symbol itself is not different depending on whether its declaration was type-only or not. To use a type-only import is not the same thing as importing only a type. You can use a type-only import to import a value. Type-only imports and exports are completely unrelated to
It’s kind of the opposite. In fact, looking at the simple example in timocov/dts-bundle-generator#290, a syntactic transform would easily produce the right output; reconstructing it from symbols is much harder. It’s a bit hard to tell what’s the right thing to expose, if anything, without a better understanding of your approach. |
@andrewbranch Sure, let me elaborate then. The tool traverses all source files from the compilation and for each node detects 1) whether it should be included to the bundle (unrelated to this problem) 2) if so, whether it should be exported and 3) if it should be exported, what names and how it should be done. As I mentioned, the first detection is not related to this question lets skip it. The second and third ones are related tho. Skipping unrelated details, statements/nodes that can generate anything in the JS file (class, function, variable, enums, namespaces) should be exported only if they are exported explicitly from the entry point. For this purpose what I'm doing is getting entry point exports, and try to find the node symbol in that table - if it is not there then it shouldn't be exported. But if it is there then it is exported and should either have Now the issue is that the fact that a node's symbol is in the exports table doesn't necessary mean that in the JS file this node is exported too. It can be exported as a type via any variation of My idea was that I can do the following:
Let me know if it is still unclear what I'm trying to do. |
Thanks for that explanation; I think I’m up to speed now.
Yeah, like I said in my last message, this isn’t accurate. The meaning doesn’t change with type-only imports/exports. You really do need Suppose we expose an API like // @filename: types.ts
interface Foo {}
export type { Foo };
// @filename: entrypoint.ts
import { Foo } from "./types";
const Foo = 2;
export { Foo }; When you read the symbol So knowing whether a symbol has a type-only declaration somewhere in its alias resolution chain isn’t enough. Really, the question is this:
Asking that question of the example above, we can see that the answer should be “no.” The symbol does have a type-only declaration in its resolution chain, but the source of the value meaning never crosses that type-only boundary, so to speak. In other words, resolving only value meanings starting from So, something that answers that question directly is probably the API we need to make. (This is a big reason why I don’t want to expose |
Oh dear, I completely forgot about declaration/symbol merging... Thanks for reminding about this wild beast 😂
To clarify, when you say "symbol with a value meaning" you mean
Maybe its still a bit off (pls forgive me, you know the context much better than me), what do you think about having this information connected to a module export instead? I know you mentioned that type-only imports/exports aren't quite related to this, but I thought that it is possible that this knowledge could be useful for module exports. What I mean is that the type checker right now can provide a list of symbols that are exported from a module ( |
@andrewbranch I think I found an example where this logic might not work. From my understanding, based on modules exports logic, any "specific" export ( export * from 'foobar'; // it exports Foo
export class Foo {} // overrides export *, no compilation error Considering this, we should first check "named" exports, and only then any export * from 'foobar';
export type { Foo } from 'foobar'; In this case, I can't get rid of a thought that "exports table" should contain this information. I really don't want to ended up writing my own modules resolution mechanism to detect if an export/import can be used as a value or type, considering how complex exports systems are and that the compiler already knows all of this. Can you help me understand why this isn't a solution we're looking for please? Thanks! |
What you just described is a bug. You should be able to access |
So "valued" export should always have higher priority over any "type" export with the same name (for the same symbol?)? In my example, even if I have |
Yeah. My intention is to have zero false positives for the “you can’t use this import because it’s type-only” error. The only relevant question to showing that error is “will it work at runtime” |
Agreed. Then it feels like your proposed question "Given an alias symbol with a value meaning, is there a type-only declaration between that symbol and the source of the value meaning?" should be corrected? E.g. in the example above there is a type-only declaration, but it doesn't really affect anything. Unless the compile will remove that "useless type-only export that doesn't affect output". |
🔍 Search Terms
"type checker", "getTypeOnlyAliasDeclaration", "detecting value vs type on a symbol"
✅ Viability Checklist
⭐ Suggestion
It would be nice if the compiler would expose
TypeChecker.getTypeOnlyAliasDeclaration
or similar function that would help to detect if a given symbol is a type or a value (e.g. a class is a type if it was imported/exported via import-type/export-type syntax.📃 Motivating Example
I'm maintaining https://github.com/timocov/dts-bundle-generator and have the following issue timocov/dts-bundle-generator#290. My problem is that I need to somehow detect if a module export symbol is a type or a value so I can detect whether the type of a symbol changes since its original declaration and its actual export so I can generate type export instead of exporting a value. I asked in the community discord and @jakebailey pointed that the type checker has function
getTypeOnlyAliasDeclaration
that might help. I've checked with my test cases and it seems it works like a charm. I understand that this function itself might not be what you actually want to expose from the API so I'm happy with either solution.Also I'm happy to contribute for this feature.
💻 Use Cases
The text was updated successfully, but these errors were encountered: