I have an app with ts-node and ts-babel-node configured (using the register require). When I try to start the app, I get the following error (app specific):
Uncaught exception: TSError: ⨯ Unable to compile TypeScript
.../verification/spec.ts (21,28): Parameter 'state' implicitly has an 'any' type. (7006)
.../verification/spec.ts (28,28): Parameter 'state' implicitly has an 'any' type. (7006)
at getOutput (.../node_modules/ts-node/dist/index.js:169:23)
at Object.compile (.../node_modules/ts-node/dist/index.js:272:19)
at Module.m._compile (.../node_modules/ts-node/dist/index.js:212:49)
at Module._extensions..js (module.js:579:10)
at Object.hook (.../node_modules/ts-babel-node/index.js:52:3)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at require (internal/module.js:20:19)
at Object.<anonymous> (.../verification/index.ts:9:13)
But when I run tsc, it gives no errors. Also, in VSCode, I see no error when viewing that file.
I managed to see why this was happening and this block caught my attention (https://github.com/TypeStrong/ts-node/blob/6748fa3780238e60c4c30e14d430d92b5bdc9f10/src/index.ts#L208):
// Add all files into the file hash.
for (const fileName of config.fileNames) {
if (/\.d\.ts$/.test(fileName)) {
cache.versions[fileName] = 1
}
}
If I remove the condition to only add the .d.ts files into the cache, and run the app, I see no errors. I think this condition is not right, as all the files should go to the cache since they are later used for getScriptFileNames in the service host (https://github.com/TypeStrong/ts-node/blob/6748fa3780238e60c4c30e14d430d92b5bdc9f10/src/index.ts#L270).
I'll try to find a minimal example to repro this since I still don't understand why is this the case, but just filing this issue with a pointer to that suspicious piece in case you maybe know more about it.
Ok I found a quick way to reproduce it. Check this repo I created: https://github.com/leoasis/ts-node-error
There are two scripts to run:
If you run npm run tsc, that will run tsc and you can verify it runs without any errors.
If you run npm run ts-node that will run ts-node start, in which case you can verify there's an error.
The problem is when there are files that are augmenting other modules, but are not directly required by any of the files in the dependency tree that is started from the starting script file.
This makes me more confident that what I mentioned in the description above is probably the culprit.
Anyway, now there's a minimal example that fails, hope that helps find the issue. Let me know if you need further info!
Adding every file resolved by TypeScript can make the project slow(er) to startup. I understand how this could cause issues, but I'm not sure if it's a bug. The reason this is there is that TypeScript and ts-node could end up running using entirely different code paths and you'll end up loading an entirely unused dependency tree. Having it support .d.ts files by default was something I added at one point because otherwise it's incredibly difficult to use ts-node. At this point and this being the first related issue, I think that intuition is still correct and I'll document this behaviour for now.
I can not think of any other places that would trigger a side-effect like that, and I think this may be a TypeScript bug - I wouldn't expect an unused augmentation to appear in my compilation (.ts or .d.ts file).
I don't think that's the correct expectation, nor a bug. In Typescript, in VSCode and even in awesome-typescript-loader this is the case for files that augment modules and are not explicitly required. I think it's not that unexpected given that you specify a directory or explicit files for TS to type check.
In any case, since this is how the other environments work, people rely on this feature, and by not supporting it in ts-node will make those projects to work differently between the editor, client side files, and node files.
Maybe this could be supported with an option to be passed to ts-node? At least it would make sense to have the option to do this.
I see. This was never a real issue in the past, pre-include and the default "include everything" behaviour, because TypeScript figured out the project from the entry points. Now that everything is considered an entry point in this case, any of it could result in a runtime failure if it were accidentally excluded. This behaviour is definitely expected in ts-node as it's been here for a year and changing it today can result in projects failing. So this sort of change is reserved for a breaking change, which could happen. A flag would be a reasonable current feature.
Now, as the breaking change, we need to consider the issues with this. Namely, a lot of startup will be consistently a lot slower. We also won't be able to use the cache anymore, so we should probably toss that out with the next major release too. The REPL, for instance, will also load a lot of unused files. Now that there's no cache and startup is slower, it might be worth considering disabling type checking at all as the default and have that behind a flag.
I'm not very familiar to how Typescript does it, but isn't there a way to reuse what they do? Is tsc slow at startup for big projects? Not sure how much of what they do needs to be reimplemented instead of used.
Anyway, will try to investigate a bit more, not very familiar with internals.
tsc can be a little slow on startup, and this may be a non-issue today. It was definitely an issue in earlier TypeScript and ts-node releases (like, way back, 1.5 or so). I did a lot then to improve perf, so maybe it's a non-issue. I do think switching the default to be fast by default might be a good idea - but I don't have good statistics on who's using it for testing vs runtime today.
I'm also having similar issues, tsc works fine but ts-node reports some errors. For example:
Message:
⨯ Unable to compile TypeScript
scripts/stream/CassandraStreamFactory.ts (12,7): Class 'CassandraStreamFactory' incorrectly implements interface 'IStreamFactory'.
Types of property 'from' are incompatible.
Type '(lastEvent: Date, completions?: Observable<string>, definition?: WhenBlock<any>) => Observable<Ev...' is not assignable to type '(lastEvent: Date, completions?: Observable<string>, definition?: WhenBlock<any>) => Observable<Ev...'. Two different types with this name exist, but they are unrelated.
Types of parameters 'completions' and 'completions' are incompatible.
Type 'Observable<string>' is not assignable to type 'Observable<string>'. Two different types with this name exist, but they are unrelated.
Property 'source' is protected but type 'Observable<T>' is not a class derived from 'Observable<T>'. (2420)
This error comes from the duplicate import of the rxjs library: after a symlink (npm link) is made tsc is building while ts-node is still failing with the above error.
@mtfranchetto That seems unrelated and tsc should also be failing there. It's a common issue with TypeScript, you can search the issue backlog over there.
@blakeembrey My testing situation is the following: I have module A and module B, both modules depend on rxjs and B depends on A. When testing/building module B tsc and ts-node give me the same error (Two different types with this name exist, but they are unrelated). After linking the rxjs version of module A to module B tsc correctly builds (since the package is exactly the same) but ts-node is still failing. Do you have any idea? Because after a couple of hours of research I can't find a reason for this except a ts-node problem.
Feel free to make a new issue about it with a repro and I can take a look. There might be something related to the language service usage that's using the symlink paths instead of the real paths. I don't believe it's related to this issue, that's all - this one is explicitly related to excluding side-effect based augmentations.
I'm having a similar issue. When I compile with tsc it works normally, but when I run my tests (mocha + ts-node) it throws:
/**/**/**/app/node_modules/ts-node/src/index.ts:307
throw new TSError(formatDiagnostics(diagnosticList, cwd, ts, lineOffset))
^
TSError: ⨯ Unable to compile TypeScript
src/controllers/**.ts (25,9): Property 'checkBody' does not exist on type 'Request'. (2339)
src/controllers/**.ts (26,9): Property 'getValidationResult' does not exist on type 'Request'. (2339)
src/controllers/**.ts (91,9): Property 'checkBody' does not exist on type 'Request'. (2339)
The same issue here
node --inspect-brk=29868 --nolazy -r ts-node/register /work/stogram-bot/src/app.ts
Debugger listening on ws://127.0.0.1:29868/82bae849-546d-4901-9016-6c3ef53d07b1
Debugger attached.
/work/stogram-bot/node_modules/ts-node/src/index.ts:307
source-map-support.js:419
throw new TSError(formatDiagnostics(diagnosticList, cwd, ts, lineOffset))
^
TSError: ⨯ Unable to compile TypeScript
source-map-support.js:422
src/routes/index.ts (34,35): Property 'body' does not exist on type 'Request'. (2339)
at getOutput (/work/stogram-bot/node_modules/ts-node/src/index.ts:307:15)
at /work/stogram-bot/node_modules/ts-node/src/index.ts:336:16
at Object.compile (/work/stogram-bot/node_modules/ts-node/src/index.ts:498:11)
at Module.require.extensions.(anonymous function).m._compile (/work/stogram-bot/node_modules/ts-node/src/index.ts:392:43)
at Module._extensions..js (module.js:646:10)
at Object.require.extensions.(anonymous function) [as .ts] (/work/stogram-bot/node_modules/ts-node/src/index.ts:395:12)
at Module.load (module.js:554:32)
at tryModuleLoad (module.js:497:12)
at Function.Module._load (module.js:489:3)
at Module.require (module.js:579:17)
@leoasis in your repro you might want to use ts-node --no-cache to make sure to run in this issue while experimenting.
As far as behavior goes, I am not sure why this would be bad or incorrect:
if (/\.ts$/.test(fileName)) {
cache.versions[fileName] = 1;
}
It might end up being slower, but to be honest I don't see why we should favor performance over correctness... but maybe I fail to understand the issue in its entirety.
I ran into the same issue today with this block of code:
throw new TSError(formatDiagnostics(diagnosticList, cwd, ts, lineOffset))
^
TSError: ⨯ Unable to compile TypeScript
TL;DR run TS-Node with --disableWarnings and the above error should be resolved.
Turns out this is a result of TS-Node stopping when there is a typings error. The default behavior for TSC is to allow the script to compile but show errors. TS-Node will instead take the same exact script and stop execution on it.
A quick example:
const f: string = "HELLO, WORLD!";
console.log(f);
f = 0; // intentional typings error
Let's do tsc test.ts || node test.js. That gives us the following:
t.ts(3,1): error TS2540: Cannot assign to 'f' because it is a constant or a read-only property.
HELLO, WORLD!
Great, TSC let us know there's a problem but also compiled the file and it ran fine. Now lets do ts-node test.ts.
throw new TSError(formatDiagnostics(diagnosticList, cwd, ts, lineOffset))
^
TSError: ⨯ Unable to compile TypeScript
t.ts (3,1): Cannot assign to 'f' because it is a constant or a read-only property. (2540)
at getOutput (C:\Users\Scott\AppData\Roaming\npm\node_modules\ts-node\src\index.ts:307:15)
.....
Well there's that pesky error again. But if we run ts-node --disableWarnings test.ts we get this:
HELLO, WORLD!
Sweet.
I'm also experimenting this issue. If I have *.d.ts files in the src code, ts-node will throw:
/home/juan/.opt/npm/n/lib/node_modules/typescript/lib/typescript.js:86795
ts.Debug.assert(outputText !== undefined, "Output generation failed");
^
Error: Debug Failure. False expression: Output generation failed
However, tsc works fine
I've just noticed it also happens when I import from an index file that exports * from other modules in same directory. Again tsc works fine, ts-node doesn't
I've also problems with reexports of declaration files.
I tried different exports but no one worked.
An example with named export:
// myDeclarations.d.ts
export interface Foo {}
// /foo/index.ts
export * from '../myDeclarations'; // Cannot find module '../myDeclarations'
An example with default export:
// myDeclarations.d.ts
export interface Foo {}
// /index.ts
import Foo from './myDeclarations.d';
export default Foo; // Cannot find module './myDeclarations.d'
I tried it with different scenarios (export type, storage folders, with and without .d) - all tries failed with ts-node (tried with 4.x.x and 5.0.1) . The typescript compiles compiles all rightly.
I use ts-node/register at mocha tests.
@Xenya0815 You'll find that if you try to run the output of tsc it'll be exactly the same. Node.js can't find a module that doesn't exist. Write your interfaces as a .ts file and don't try to import from .d.
@blakeembrey I can not confirm that.
To reproduce my result, here what I did to check it:
// src/myDeclarations.d.ts
export interface Foo {
foo: number;
}
// src/index.ts
import {Foo} from './myDeclarations';
export default Foo;
const x: Foo = {
foo: 42,
};
console.log(x.foo * 2);
The command to compile: tsc src/index.ts --declaration --declarationDir dist/@types --target ES2016 --module commonjs --outDir dist/component (I tried it without target and module also: same result)
tsc version: 2.7.2
Created files:
// dist/@types/index.d.ts
import { Foo } from './myDeclarations';
export default Foo;
// dist/component/index.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const x = {
foo: 42,
};
console.log(x.foo * 2);
When I run node dist/component/index.js, it returns with the status code 0 and I see the output 84.
So the typescript compiler removes the import of the *.d.ts file even the import was reexported.
The myDeclarations.d.ts was not created at dist/@types, that is right. But that does not matter at executing with Node.js. I copy that available *.d.ts before I release the types.
Did you enable --type-check? I'd guess tsc does elision of modules based on type detection. This isn't something I can fix, I'd recommend using .ts files or type checking.
Adding a note about this to the README now.
I'm also going to close this issue since it's been a bit derailed. If someone has a reproduction of this (an error code of 0 with tsc and node on the output files and non-zero error code of ts-node on the same code), please open a new issue 😄
I do not like to comment on an old and closed issue, however, I run into this issue this week and it's on the top of my Google search result.
I have been using ts-node for many years but it still wasted me many hours to figure this problem out, because I'm working on a new Redux typing project, there are too many new things. After including the types.d.ts in the .ts it works like a charm.
So my suggestion is: Could we add an error message to notify the user to check whether they forget to include the reference path in the .ts file, if we found any .d.ts file in the source directory?
@huan it sounds like you want to enable --files mode, which will eagerly load everything matched by includes and files, the same way tsc does. Rather than throwing an error, this will fix the source of the error.
The alternative, throwing the error, involves extra filesystem calls, which imposes a performance hit.
Most helpful comment
I've just noticed it also happens when I import from an index file that
exports * fromother modules in same directory. Again tsc works fine, ts-node doesn't