Comments (57)
And, of course, @ericdrobinson: Thanks a lot (again) for your contributions. The great thing is that not only does this increase the quality of the typings, but I also feel like I have a better understanding of how TypeScript works now, so thanks on that "front" as well 👍
from typings.
The problem I have with this is that this would – if I'm not mistaken – "suggest" that one can actually import this type (and possibly use it for instanceof type checking etc.).
@pklaschka In this case, you are indeed mistaken. It took me a while to understand this as well, but there is a very important distinction between a class
and just about everything else.
When you define a class
, you are defining both the type
AND a constructor function with the same name as that type
. It's something like doing the following:
interface MyObject {
new (): MyObject;
}
let MyObject: MyObject;
For a more complete example, check out the type declaration for Object
in lib.es5.d.ts. Here's what's happening:
- Declare
Object
andObjectConstructor
types. These interact with one-another. - Declare the ambient constant
Object
name (which is typed asObjectConstructor
).
By declaring the constant Object
name you are now telling TypeScript that users can access that directly. If you only had the interface
s (or type
s), then you only have TypeScript type names and attempting to add then to the right side of an instanceof
will result in an error because there is no name associated with it.
So, when you declare a class
, you are actually defining both the type and the name, which allows the "type" to appear on the right side of the instanceof
keyword in script without error.
I've now solved this by moving those typedefs to the global namespace (in the
index.d.ts
). It passes the linters in VSCode and WebStorm – see 07a903c.
Please give the version I suggested a try. The global approach is strictly worse for two reasons:
- The
type
declaration is globally accessible, which should make it 100% available to every context, not just ones wherein yourequire('interactions')
. - The
type
declaration is no longer located in the module within which it is documented.
In my tests (in VSCode) with the type
declarations defined in a module within interactions.d.ts
, the following code results in an error:
// ERROR: Property 'Interaction' does not exist on type 'typeof interactions'.
let x = selection instanceof require('interactions').Interaction;
Which is expected as the type
declaration did not define a type name. If WebStorm actually suggests that Interaction
is a valid name to use in this case, then this is a bug in WebStorm.
For the record, VSCode's IntelliSense doesn't even suggest the type
s in autocomplete:
from typings.
@ericdrobinson Understood. This, then, holds true for other modules as well, doesn't it? Specifically, I'll have to change application
, clipboard
, commands
, and viewport
as well, correct?
from typings.
@ericdrobinson Understood. This, then, holds true for other modules as well, doesn't it? Specifically, I'll have to change
application
,clipboard
,commands
, andviewport
as well, correct?
As done in
d663c5a 2b3ea03 63574ec 37b07a1
from typings.
Alright, in that case, this issue should be get resolved with the PR as well. If there are no more issues within the next two hours 😆, I'll merge the PR...
from typings.
Was about to write something along the lines of "Yes" but you beat me to it!
If you attempt to do the following in XD you get an error:
const viewport = require('viewport');
let v = new viewport(); // <-- Plugin TypeError: viewport is not a constructor
So these should be declared as module
s. 😄
from typings.
@pklaschka Happy to help! I'm very glad to hear that the conversation has helped you advance your understanding of TypeScript. That was a large portion of the motivation to provide suggestions with explanations instead of a monolithic PR with everything already packaged up.
And, honestly, this process clarified more TypeScript stuff for me as well! 😄
The one final question that I have is about the let module: {exports:any};
global declaration in index.d.ts
. That doesn't appear to be necessary [in VSCode]. Removing it changes nothing as module
is already defined (in VSCode, at least?). Do things go poorly for you in WebStorm if this is removed?
from typings.
@ericdrobinson Nope. Unfortunately, WebStorm lets you decide if you
- activate NodeJS support for the project (which means you gain access to all the default Node modules and that it asks you if you want to run
npm install scenegraph
since it is an "unresolved" module) - deactivate this option for the project (which means WebStorm doesn't know those two anymore).
Therefore, as long as this (or these – counting require()
as well) doesn't interfere with VSCode stuff, I'd like to leave it in to "explicitly" allow for CommonJS module im-/ exports.
from typings.
Sounds good to me! I expected that it might be an environmental thing. Still not entirely sure where VSCode infers that definition from, but c'est la vie.
from typings.
Also, two more things:
- Why does
index.d.ts
have theexport {};
line? - Why does
scenegraph.d.ts
use the more standardexport {...}
format instead of theexport =
approach that all other modules use?
from typings.
At least I now think I've got an idea of why WebStorm couldn't resolve this before. Somehow, changing the branch messed up my project configuration (no idea why), resulting in some strange way of WebStorm interpreting only parts of it... Sorry about that.
from typings.
@ericdrobinson Doing so right now 😉
from typings.
Cool. I'll take a look. I've spotted something... unexpected with this route that I want to check on.
from typings.
Okay, so it looks like declaring a class within a module will also cause it to export the name. If we want a type to exist in/for a module, then we need to use interface
instead of class
. There are ways to get an interface
to work like a class
, of course, but if the class
isn't "public" in the module anyway, then it is probably not meant to be new
ed up.
Seems as though your approach of converting any class
that shouldn't be exported into an interface
is indeed the correct approach.
I tested the uxp.shell
instance's constructor and, while it exists, it's name is "b" and almost certainly isn't meant to be used... 😜
from typings.
Repo Structure - New Issue?
I would like to suggest that the
jsconfig.json
andsample.js
file get moved into asample
orexample
folder that's separate from the top level. Atsconfig.json
file at the top level then makes the most sense. This will allow a clear separation of TypeScript and JavaScript files while allowing you to have atsconfig.json
file that's designed specifically for writing the declaration files.
The thing is that this would mean that the sample.js
file wouldn't work when cloning the repo, anymore, since the jsconfig.json
is meant to be placed at the project root folder with a types
folder as a sibling (the way it is now). This makes using the typings for JS basically a copy-and-paste solution in VSCode as one simply has to copy types
and jsconfig.json
into the project directory to use it.
Therefore, while I understand the request, it's a bit more complicated than that and I'd, therefore, suggest having this in a new issue discussing solutions and "apart" from the current Pull Request.
Inaccessible Types Complication
I did a quick test of the
scenegraph
module and got the following:// Output: // GraphicNode,Rectangle,Ellipse,Line,Text,Path,Group,Artboard,BooleanGroup,RepeatGrid,SymbolInstance,LinkedGraphic,Matrix,Color,LinearGradient,RadialGradient,ImageFill,Blur,Shadow,root,selection,Polygon console.log(Object.getOwnPropertyNames(require('scenegraph')));When you compare those to the list of types currently available from
scenegraph.d.ts
you will notice thatRootNode
andSceneNode
are not available.This is a bit more complicated because classes "extend" from
SceneNode
(at least). This would need to change toimplements
for the sub-classes and then they would need to add all the properties.Boo. I'm still playing around with this...
Interesting – one of my projects actually relies on such a type check. I guess it's time to hotfix that. However, I'd like to defer this into another pull request (as soon as we find a better-suiting solution) to not explode the current PR even further than it already is. Since the PR is mostly structure-related and this is an actual "content" mistake, that sounds like something that's going to be easy to merge with the next bigger update.
Contributor Info
Thanks! I appreciate it.
- Name: Eric Robinson
- Email: eric - at - sonicbloom - dot - io
- URL: https://github.com/ericdrobinson
Perfect, thank you. You're now added to the contributor list.
I'll now merge the Pull Request and open new issues for the two ideas to keep this (slightly) organized. After all, this is the 56th comment on this issue and we're already far off-topic from the original issue title 😉
from typings.
Yes! The problem you have here is that the Interaction
type is not included in the exported class
. This is another instance wherein it is odd that you are exporting a class when you can never have instances of type interactions
:
Whenever you export a class, you make its constructor available to importers.
Lots "classes" in the XD API do not allow instances. At the very least the constructor should be marked private
. That said, these APIs are more likely intended to be accessors/modifiers and in such a light, treating them as members of a module or namespace is better aligned with the goal, I think. It's also frequently better aligned with reality. See the following:
// OUTPUT: Object
console.log(require('interactions').constructor.name);
The interactions
module doesn't even have its own class...
In this case, I would suggest the following:
- Convert the
interactions
class
into amodule
. - Convert both
allInteractions
andhomeArtboard
properties intoconst
declarations.- Behind the scenes these are actually implemented as getters with no setters, but that is more of an implementation detail. The result for the consumer of the API is identical.
- Change the
homeArtboard
property declaration to:as theconst homeArtboard: Artboard | null;
?
in Adobe's type annotations apparently means that value can benull
rather than specificallyundefined
. - Move all of the
type
declarations into theinteractions
namespace.
The above done, you will be able to import
the Interaction
type into scenegraph
via one of the following methods:
- Top-level import:
import { Interaction } from 'interactions';
- Using
import
-types:readonly triggeredInteractions?: Array<import('interactions').Interaction>;
Either way should work fine now!
from typings.
@ericdrobinson The problem I have with this is that this would – if I'm not mistaken – "suggest" that one can actually import this type (and possibly use it for instanceof
type checking etc.). Since this is not possible for a plugin (as this isn't "more" than a type definition, making it "importable" would be wrong...
from typings.
I've now solved this by moving those typedefs to the global namespace (in the index.d.ts
). It passes the linters in VSCode and WebStorm – see 07a903c. @ericdrobinson Does that work for you? While it's probably not a very nice solution, it works without having "unreal" exports...
from typings.
- Why does
index.d.ts
have theexport {};
line?
No idea, fixed in 8f7bd4f
2. Why does
scenegraph.d.ts
use the more standardexport {...}
format instead of theexport =
approach that all other modules use?
Again, I have no idea. Whyever, it appears to work for scenegraph (imports work without any problems)... I have no idea what the decisive difference between uxp
and scenegraph
is, but it works 🤔 This is probably one of the first modules I've written, so it really was just trial and error without any kind of thought behind it back then. It appeared (and still does to this day) to work and that's all I know...
from typings.
I have no idea what the decisive difference between
uxp
andscenegraph
is, but it works 🤔
I hate to rehash this now after uxp
works, but does changing things in uxp.d.ts
for you work as expected?
You mentioned here that you used export =
because it was the closest way to implement the CommonJS module export pattern.
As I understand things, using export =
is a bit of a kludge and should go away as JavaScript evolves towards an ES2015+ module world. In my local environment, I tested modifying uxp.d.ts
in the following manner:
- Remove the wrapper
declare module uxp
. - Add
declare
to both theshell
constant and thestorage
module. - Change
export = uxp;
to the following:export { shell, storage }
I saw no errors in VSCode. Do you see errors in WebStorm if you try this pattern?
from typings.
@ericdrobinson I have no idea why, but now, this works... Does that mean that all the other things (application etc.) should get exported another way as well?
from typings.
Does that mean that all the other things (application etc.) should get exported another way as well?
Basically, yes. But that said, it would be better, I think, to try a different approach. Please let me know if this works for you in scenegraph.d.ts
and uxp.d.ts
(two good test cases):
- Remove the existing
export
statement. - Replace
declare
withexport
.
This is a quick test - the only items that should actually be exported are those that you want visible from outside of the module (read: internal types should not be exported). However, find-and-replace is a quick way to get a sense of whether this works or not...
from typings.
@ericdrobinson Yep, that works.
from typings.
Doing the above in VSCode changes the suggested property interpretation.
Individual export ...
Version:
Resolved names are different because of the global find-and-replace for a quick test.
For reference, those icons mean "Variables and Fields" and "Classes" respectively.
from typings.
Ok. That makes sense so far.
from typings.
If you're okay with it, I would suggest changing the export process to be of this latter approach (export class X
, export interface Y
, export const Z
, etc.). It provides you with supreme control as to whether or not the type will be available outside of the module without needing special workarounds.
from typings.
PR should now be up-to-date with this way of exporting... 😅
from typings.
@ericdrobinson If everything's working for you, I'd go ahead and merge the PR. Writing the changelog for this release will take long enough as it is – I don't need any further things to put into one PR 😆
from typings.
Agreed. That said, I'm seeing something unexpected (and struggling to understand it). Specifically, the following:
const uxp = require('uxp');
uxp. // <-- For autocomplete suggestions...
results in this in VSCode:
But... the Shell
class isn't exported. 😕
from typings.
@pklaschka Okay, I figured it out. The plugin approach seems to work for the XD APIs by doing the following:
declare module 'scenegraph'
{
interface Point {
// ...
}
Basically, wrap the entire module in declare module
and use a string version of the module name: 'scenegraph'
instead of scenegraph
.
Can you give that a shot and see if it works for you with WebStorm?
from typings.
Also, please be careful to test with a strict tsconfig.json
file around to catch errant declare
s, etc.
from typings.
@ericdrobinson Unfortunately, that doesn't work in WebStorm. It doesn't allow for checks like node instanceof Text
which are, of course, vital for plugin development.
from typings.
It works for the uxp
module, though. Also, everything else works. It's only instanceof
that breaks with this.
from typings.
@pklaschka Let me look into it a bit. I say that because I think this is the "correct" path. The structure I suggested appears to be how you're 'supposed' to declare ambient modules.
Let me quickly check the instanceof
case with scenegraph.d.ts
...
from typings.
Correction: All hell breaks loose in WebStorm when I do that. It simply "accepts" everything as "correct" code and does no type checking or autocompletion whatsoever...
from typings.
@ericdrobinson I was able to get everything to work as expected by having
interface Shell {
[...]
}
instead of
declare class Shell {
[...]
}
cf. b0a1352
from typings.
Did you try:
class Shell {
[...]
}
?
The problem should have been the declare
statement, not that it was an interface
vs class
...
from typings.
I mentioned above that you need to get rid of declare
when you wrap the modules in declare module 'name'
but you may have missed it...
I also suggested that you re-add the tsconfig.json
file so that you can be notified about such errors. 😄
from typings.
"A declare modifier is required for a top level declaration in a .d.ts file" for class Shell
– I've added the tsconfig.json back in a few commits ago 😉
from typings.
from typings.
Just to be clear: By
I was able to get everything to work as expected by having
interface Shell { [...] }instead of
declare class Shell { [...] }cf. b0a1352
I meant without the "ambient module" declarations...
from typings.
I meant without the "ambient module" declarations...
How about with the "ambient module" approach?
from typings.
from typings.
How about with the "ambient module" approach?
=>
Correction: All hell breaks loose in WebStorm when I do that. It simply "accepts" everything as "correct" code and does no type checking or autocompletion whatsoever...
from typings.
Does that include removal of the declare
keyword when you made the move?
Also, did you try restarting WebStorm? (I've had to do that with VSCode in the past to get the language server to fully refresh...)
from typings.
Does that include removal of the
declare
keyword when you made the move?Also, did you try restarting WebStorm? (I've had to do that with VSCode in the past to get the language server to fully refresh...)
Unfortunately, yes. The problem remains.
from typings.
With the shell being an interface (and no ambient modules), however, everything appears to be working as expected in WebStorm and VSCode.
(basically the way it is at b0a1352).
According to the documentation, Shell
should be a global class. However, new Shell()
also doesn't work in XD, meaning it could actually be an interface...
from typings.
Yes, but the type information is incorrect. Shell
actually is a class, if memory serves.
from typings.
Let's provide an extremely simple test case. Please do the following:
- Create a new file called
uxp2.d.ts
. - Add this script to it:
class Shell {} declare module 'uxp2' { export const shell: Shell; export namespace storage { const fileSystem: any; } }
- In your
sample.js
file, try the following:const uxp2 = require('uxp2').storage.fileSystem;
Do you get any errors with any of that?
from typings.
According to the documentation, Shell should be a global class. However, new Shell()
also doesn't work in XD, meaning it could actually be an interface...
from typings.
Do you get any errors with any of that?
It isn't that I get any errors with ambient modules. It is the fact that I'm not getting ones, e.g.,
const notMyShell = require('uxp');
notMyShell.openExternal('...');
Doesn't show an error. Basically, WebStorm just "gives up" on understanding the typings with the ambient modules and still provides some autocompletion, but doesn't actually "understand" the type flow (an object is of a certain type b
in an if (a instanceof b)
block, uxp
does not equal uxp.shell
etc.)
from typings.
Let's provide an extremely simple test case. Please do the following:
- Create a new file called
uxp2.d.ts
.- Add this script to it:
class Shell {} declare module 'uxp2' { export const shell: Shell; export namespace storage { const fileSystem: any; } }- In your
sample.js
file, try the following:const uxp2 = require('uxp2').storage.fileSystem;Do you get any errors with any of that?
from typings.
Yeah, that actually should be declare class Shell
. Restarting my VSCode instance revealed that same error (which is good!) 😄.
from typings.
With the ambient module, the wrong code shows no error:
but without this module declaration, everything works as expected:
from typings.
I tested the
uxp.shell
instance's constructor and, while it exists, it's name is "b" and almost certainly isn't meant to be used... 😜
I actually just tested this myself and wanted to write the exact same thing.
So, I'll leave it the state it is in (b0a1352) and merge it?
If you'd like / you agree, I'd like to add you to the contributors list in the package.json
(since credit is definitely due to you here). For that, I would need name, url and email address of your choice:
{
"name": "",
"email": "",
"url": ""
}
from typings.
A few last minute items!
Repo Structure - New Issue?
I would like to suggest that the jsconfig.json
and sample.js
file get moved into a sample
or example
folder that's separate from the top level. A tsconfig.json
file at the top level then makes the most sense. This will allow a clear separation of TypeScript and JavaScript files while allowing you to have a tsconfig.json
file that's designed specifically for writing the declaration files.
Thoughts?
Inaccessible Types Complication
I did a quick test of the scenegraph
module and got the following:
// Output:
//
GraphicNode,Rectangle,Ellipse,Line,Text,Path,Group,Artboard,BooleanGroup,RepeatGrid,SymbolInstance,LinkedGraphic,Matrix,Color,LinearGradient,RadialGradient,ImageFill,Blur,Shadow,root,selection,Polygon
console.log(Object.getOwnPropertyNames(require('scenegraph')));
When you compare those to the list of types currently available from scenegraph.d.ts
you will notice that RootNode
and SceneNode
are not available.
This is a bit more complicated because classes "extend" from SceneNode
(at least). This would need to change to implements
for the sub-classes and then they would need to add all the properties.
Boo. I'm still playing around with this...
Contributor Info
Thanks! I appreciate it.
- Name: Eric Robinson
- Email: eric - at - sonicbloom - dot - io
- URL: https://github.com/ericdrobinson
from typings.
Repo Structure - New Issue?
I would like to suggest that the
jsconfig.json
andsample.js
file get moved into asample
orexample
folder that's separate from the top level. Atsconfig.json
file at the top level then makes the most sense. This will allow a clear separation of TypeScript and JavaScript files while allowing you to have atsconfig.json
file that's designed specifically for writing the declaration files.The thing is that this would mean that the
sample.js
file wouldn't work when cloning the repo, anymore, since thejsconfig.json
is meant to be placed at the project root folder with atypes
folder as a sibling (the way it is now). This makes using the typings for JS basically a copy-and-paste solution in VSCode as one simply has to copytypes
andjsconfig.json
into the project directory to use it.Therefore, while I understand the request, it's a bit more complicated than that and I'd, therefore, suggest having this in a new issue discussing solutions and "apart" from the current Pull Request.
moved to #57
Inaccessible Types Complication
I did a quick test of the
scenegraph
module and got the following:// Output: // GraphicNode,Rectangle,Ellipse,Line,Text,Path,Group,Artboard,BooleanGroup,RepeatGrid,SymbolInstance,LinkedGraphic,Matrix,Color,LinearGradient,RadialGradient,ImageFill,Blur,Shadow,root,selection,Polygon console.log(Object.getOwnPropertyNames(require('scenegraph')));When you compare those to the list of types currently available from
scenegraph.d.ts
you will notice thatRootNode
andSceneNode
are not available.
This is a bit more complicated because classes "extend" fromSceneNode
(at least). This would need to change toimplements
for the sub-classes and then they would need to add all the properties.
Boo. I'm still playing around with this...Interesting – one of my projects actually relies on such a type check. I guess it's time to hotfix that. However, I'd like to defer this into another pull request (as soon as we find a better-suiting solution) to not explode the current PR even further than it already is. Since the PR is mostly structure-related and this is an actual "content" mistake, that sounds like something that's going to be easy to merge with the next bigger update.
moved to #56
from typings.
Related Issues (20)
- Reimagine structure with samles etc. HOT 4
- JSDoc / TSDoc / Comment Spec HOT 4
- LinearGradient and RadialGradient do not exist in scenegraph HOT 1
- Missed properties for RadialGradientFill HOT 1
- RenditionSettings make format specific properties optional
- make RenditionSettings.type strongly types HOT 2
- RenditionSettings.outputFile
- tsconfig.json incorrectly relies upon browser DOM types HOT 2
- Is `Line.setSTartEnd` typo? HOT 1
- 'SceneNode' refers to a value, but is being used as a type here ts(2749) HOT 13
- SceneNode doesn't contain addChild, but all derived classes/types do, shouldn't it be on SceneNode itself? HOT 2
- XD 25: application.activeDocument
- Suggestion: Call out custom type name overrides in ReadMe HOT 1
- Why add both tsconfig.json and jsconfig.json? HOT 15
- "Typings" is an Archaic Term [and Field Name] (package.json)
- using with`@types/node` inflicts compiler error about duplicate definition HOT 3
- Update typings to XD 29
- A couple type problems HOT 2
- Is this project still active? HOT 3
- Typings for Photoshop? HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from typings.