-
Notifications
You must be signed in to change notification settings - Fork 106
Support export * from '..' pattern from declaration files
#1220
base: master
Are you sure you want to change the base?
Conversation
Previously tsickle skipped such statements in declaration files. This
changes it so that it now generates `@type {(typeof ns1|typeof ns2|...)}`
jsdoc on mangled namespace, which allows closure compiler to find
properties defined on any of namespaces.
|
Can you describe how you're verifying whether this works? Another way of asking that is, what problem is this fixing for you, and how do you evaluate whether this fix fixes that problem? In some other place we implement |
|
I'm having compiling some user code using tensorflow/tfjs as an ultimate test in mind, but there are other issues that is preventing it, so I'm currently just trying with simple toy cases with hand-written externs, and checking whether closure compiling it does not mangle properties defined in externs and does not produce warnings. I've added one such case as a golden test, I'll check if I could make it more robust. The main reason I chose this way was that it seems to be requiring the least changes to the existing code which I do not know well. I see that traversing re-exported module would work well too. But that way would produce much larger externs file when there are multiple |
|
Regarding this approach's magic, I think if tsickle changes the way it generates externs to generate one big interface per file and declare namespace objects as having type of the interface, I suppose it can express intersection types in more straightforward way via multiple |
|
I've tried to create a clear-cut example which this patch would fix, but it turned out to be hard. It's partly because what tsickle depends on undocumented behavior of closure compiler. See the above mentioned issue google/closure-compiler#2132. Because this pattern is what tsickle generates, even if tsickle does not properly communicate type information to closure compiler, broken externs still prevents renaming of some properties altogether. I hope this situation is improved, and in this regards, I think tsickle should generate externs in a form recommended in the issue above (a big interface for each files), for better minification result. And once this is done, the current state of skipping |
|
I tried tinkering with the big namespace per file idea, and created files like But the latter file seems to let me assign anything to fields of ns (like the nonexistent Q) and doesn't think ns.X or ns.Y exist. What did I do wrong? |
|
What I was suggesting is a big interface per file. [edit: removed a comment which turned out to be irrelevant] |
|
I am not too great on the details here (I don't think Closure thinks about interfaces and namespaces the same way as TS does) but I am not sure if a big interface per file can have interfaces as fields. That is, in my above code I was attempting to simulate what should happen when you merge a library with an "X" export with another with a "Y" export. |
I hope there is a way to do that. I don't understand what your example is testing -- is it about this PR or about the new idea of interfaces per file? |
|
Both, I thought? I was trying to understand what this PR does by making a small example of the code pattern is relies upon, but I couldn't seem to make it work. I think maybe I misunderstand what this is doing. |
|
I now understand your example. You are right that However, I think that such a code pattern cannot be generated from tsickle. When tsickle generates types, it seems to be always resolving to the actual place where a symbol is defined, so it will be , I guess this is how ts API works. Regarding |
|
(Edit: moved to #1223) |
The main change is to make jsdoc types that are exported somewhere
always point to the actual place of definition instead of any of
intermediate `export *`s.
The necessity of this change is that with the previous commit, tsickle
generates `{(typeof ns1|typeof ns2)}` type annotation for certain
namespaces in externs. There is no problem with resolving values on such
namespaces, but it appears that the closure compiler does not resolve
types on such namespaces. Therefore, when a namespace to be used in type
annotation is to be determined, it always needs to be a namespace on
which that type is actually defined.
This contract has been abided for `.ts` transformations (fortunately),
but wasn't for externs generation. Therefore it changes `addImportAlias`
function to use the method used in `.ts` transformations.
Type --(Type.symbol)--> symbol --TypeTranslator#symbolToString()--> name
|
Hi, upon further consideration I've noticed that the aforementioned premise
is not abided in externs generation. Fortunately it seems to be working on normal So I fixed it from Now there is a subtle problem. As I've checked, this /** @const {typeof B} */ var A;
/** @const {typeof C} */ var B;
/** @const */ var C = {};
/** @type {number} */ C.C;produces a compiler warning, /** @const */ var C = {};
/** @type {number} */ C.C;
/** @const {typeof C} */ var B;
/** @const {typeof B} */ var A;works, as expected. /** @const {typeof B} */ var A;
/** @const */ var C = {};
/** @type {number} */ C.C;
/** @const {typeof C} */ var B;It would only be reliable when externs are concatenated in some topological order. This is provably possible when there's no circular dependencies among |
This checks that an interface declared behind several nested export stars are referenced in heritage clauses by the namespace of the file the interface is first defined, not on ones of any of middle reexports.
|
Is there anything that I can do to help the review process? I intend to provide more patches for other module-dts issues (visible ones) building on top of this and #1221. |
As a first step in full module d.ts support, I attempted to provide support for
export * fromstatements in externs generation. I would appreciate any feedbacks.Changes
When there are
export * from '...'statements in a file, generate externs on a different namespace, similar to what is done forexport = '...'statements. The original namespace is declared as having a union type of each of namespaces on which exported members of re-exported modules are defined. For example,would generate:
Considerations
As I understand, the original namespace's type would be more like an intersection type than a union type, but since closure type system does not have intersection types, and according to https://github.com/google/closure-compiler/wiki/Types-in-the-Closure-Type-System#union-types,
so I suppose the compiler will be able to find all the property on such union types, as it would find on intersection types.
A change made is that now
generateExternsdepends on the fullprogram, in order to check whether other files that it reexports actually have generated namespaces.An edge case that might be an issue is when multiple reexported module declares the same name. As I understand, this is not forbidden in ES module specification, and the one exported later overrides the former. I do not know how closure compiler would behave in such cases. But I guess no one would intentionally write such code and it'd be possible to fix it from
.d.tsside, so it would not be a big issue.Another way I've considered is to make such mangled namespace object implement some interfaces, which as I understand is a preferred way of writing externs according to google/closure-compiler#2516 (comment). Then the type of the root namespace could be a union type of such interfaces.