Skip to content
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

setTimeout or setInterval type issue when using --watch #348

Closed
mrkmg opened this issue Oct 28, 2016 · 14 comments
Closed

setTimeout or setInterval type issue when using --watch #348

mrkmg opened this issue Oct 28, 2016 · 14 comments

Comments

@mrkmg
Copy link

mrkmg commented Oct 28, 2016

Not sure if this is a bug in ts-loader, but I figured I would start here.

Package Versions
@types/node: 6.0.46
ts-loader: 0.9.5
typescript: 2.0.6
webpack: 1.13.3

As you can see, I am using @types/node for NodeJS type definitions.

The issue I am having is with setTimeout and setInterval. In @types/node, the "type" of the number that is returned from setTimeout or setInterval is "NodeJS.Timer". The following is valid and builds perfectly when run with just webpack.

let t: NodeJS.Timer;
t = setTimeout(() => console.log("test"), 1000);

The problem is when I run webpack --watch. On the initial build, everything is fine. Once I change the file which triggers a re-build, I get the following error:

ERROR in ./test.ts
(4,1): error TS2322: Type 'number' is not assignable to type 'Timer'.

It appears to be that on rebuilds, a different type definition is being used, one which defines the return of setInterval and setTimeout to be <number> instead of <NodeJS.Timer>. Obviously I can work around this, but seeing as these are standard functions in node and @types/node is a widely used type definition of the standard node library, it would probably be best to resolve this issue.

I have created a very minimal example to demonstrate the problem.

How to Reproduce

  1. Clone https://github.com/mrkmg/webpack-ts-loader-timer-error-example
  2. Run npm install or yarn install
  3. Run webpack --watch
  4. Change the file test.ts. (I just added a newline and hit save)
@johnnyreilly
Copy link
Member

Should you not be using window.setTimeout? Remember WebPack is expecting to be used in a browser environment and not in a node environment. setTimeout in node are different to the browser I believe...

@mrkmg
Copy link
Author

mrkmg commented Oct 28, 2016

WebPack is awesome, and I found can be used to generate a single file, even for use outside a browser.

For my use case, I am using WebPack to build a single file, which can then be executed by nodejs. In fact, I am prepending "#!/usr/bin/env node" to the top of the file and removing the extension as well so I have a "shell" script.

Everything so far has been perfect with WebPack for this use-case. The only issue is this setTimeout issue.

What baffles me is that the first build works flawlessly, but on the subsequent builds I get the error. Like I said above, it feels like a different type definition is being used for builds triggered via watch.

@johnnyreilly
Copy link
Member

Wow - that's a super unusual use case for WebPack. What's the benefit of using it though? If it's node then commonjs means your require/import statements in TypeScript will just work. Does WebPack actually add anything extra? If I was developing something like that I'd probably be using tsc --watch and have done?

@mrkmg
Copy link
Author

mrkmg commented Oct 28, 2016

The resulting script will be "installed" like a typical program into /usr/bin. Then it will be able to be executed by various users. In order to accomplish this I examined the following options:

  • Install all the dependencies into /usr/bin.
    • Obviously this is a terrible choice. /usr/bin is not the appropriate place.
  • Install all the dependencies globally with npm.
    • At first this seems fine, but you quickly realize that individual project dependencies do not belong in global. Too many chances for one to end up the wrong version, or uninstalled.
  • Bundle the dependencies into the application
    • This is what I am doing currently. It removes any worry that a user will be missing dependencies or have the incorrect versions. All I would have to do is replace the script in /usr/bin and the user does not have to worry about anything.

I think it's apparent that there is more going on than just the simple explanation above, but it gives you an idea of why I would decide to do this. At the end of the day, WebPack is allowing me to have a single distributable file with only depends on node, instead of node, npm, and all the dependencies of the project as a whole. I like to think of it akin to static compilation in C#. Create a single executable with no external dependencies.

I am aware that many licenses on node modules would frown on this type of distribution. This project, while being open source, would not distribute that "compiled" file. It would only provide directions for building the file and using it for your specific needs.

I hope this makes things a little more clear. I have tried other solutions like nexe or enclose, but honestly WebPack was much more configurable and allowed for the whole process to be inlined to a single command to webpack. The webpack --watch made it even better. That is until I used a setInterval.

@johnnyreilly
Copy link
Member

Wow. That's pretty cool!

@mrkmg
Copy link
Author

mrkmg commented Oct 28, 2016

Okay, so a little more digging and I think I have found the culprit.

The typescript package contains the following: https://github.com/Microsoft/TypeScript/blob/master/lib/lib.d.ts#L18631-L18632

Now the question is, why does the first run of webpack use the appropriate file, but the subsequent runs do not?

@johnnyreilly
Copy link
Member

I don't know but I'm wondering if it's a tsc issue rather than ts-loader?

@mrkmg
Copy link
Author

mrkmg commented Oct 28, 2016

Just to test, I put "noLib": true in my tsconfig and I no longer got the issue. But pretty much everything else broke. Obviously not a solution, but confirms my suspicion that the issue is that for some reason when using webpack --watch, runs triggered by file changes are for some reason not properly resolving the correct type definition.

Still digging.

@johnnyreilly
Copy link
Member

isn't lib somewhat modularised now? Can't you pick the bits you're interested in as of 2.0? using the lib flag?

@mrkmg
Copy link
Author

mrkmg commented Oct 28, 2016

Well, I think I have a work-around that I can live with.

Instead of using setInterval from the "Global" object, I am explicitly importing it.

This works, even for with "--watch"

import {setInterval} from "timers"
let t: NodeJS.timer;
t = setInterval(() => console.log('test'), 1000);

This doesn't

let t: NodeJS.timer;
t = setInterval(() => console.log('test'), 1000);

@mrkmg
Copy link
Author

mrkmg commented Oct 29, 2016

Not sure if I should close this. Probably not an issue for many people.

@johnnyreilly
Copy link
Member

Probably not - I'll close this. But I'm glad you've got something that works!

@angularsen
Copy link

Just stumbled across this, so this is still an issue, but the workaround works nicely!

@edvinkuric
Copy link

edvinkuric commented Nov 19, 2017

Haha, i just had the same Issue...
Did the same thing as workaround:
import { setInterval } from "timers"; const gameLoopInterval: NodeJS.Timer = setInterval(gameLoop, 0.01);

really weird behaviour though

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants