Closed samreid closed 2 years ago
For now, perhaps we should use --clean
for builds, since it is important to catch all the errors. The code output is the same, but it can be important to error out if there is a type error. But maybe we would catch it in the IDE in advance, but it seems flaky for the builds to not catch this. Also, if we run --clean
all the time, it will significantly hamper our workflow.
I wonder if this overlaps with the caveat in https://www.typescriptlang.org/docs/handbook/project-references.html#caveats. Do we need to make sure noEmitOnError is truly enabled?
@jonathanolson and I discussed briefly. The next steps for this issue are:
Surprisingly, the self-contained example does not exhibit this problem. (Note that it is easier to switch between the tandem being a string vs any). I'm not sure if I have the tsconfigs matching our style exactly, or where the essential difference might be. Multiple layers to get to the dependency?
Multiple layers to get to the dependency?
That did the trick. I introduced an intermediate transitive dependency and now can reproduce the problem in an isolated case. I'll show a patch for this failing example, perhaps we can rename it to something sensible for sharing in a TypeScript issue.
Instructions:
tsc -b --clean && tsc -b
in gravity-and-orbits. This succeeds because tandem has type any
string
.tsc -b
in gravity and orbits. It should fail, but it succeeds.Note in the last step that tsc
fails. And also tsc -b
correctly fails if you don't have the intermediate transitive dependency (in this case, joist).
Here's a renamed version that uses A,B,C:
Instructions:
tsc -b --clean && tsc -b
in projectA. This correctly fails because name
has type string
and hence has no arbitraryFakeMethod
.any
tsc -b
in projectA. It succeeds.tsc -b
in projectA. It succeeds but should fail.Note that switching projectA's tsconfig references to depend on projectC directly instead of through projectB correctly identifies the problem. The same problem occurs if projectC is a *.ts file C.ts instead of C.js (adding the field declaration as appropriate for each test).
Next steps:
tsc -b && tsc
? How would that interplay with incremental?Draft write up before creating a TypeScript issue:
Issue title: # tsc -b incorrectly succeeds when there is a problem introduced by a transitive dependency
project references
transitive
dependency
stale
Reproducing the problem requires transitive project references, and running tsc -b
from the command line, so I don't believe it can be done in the TypeScript playground.
My team observed that tsc -b
can incorrectly succeed when a problem is introduced by a transitive dependency. In fact, tsc -b
can give two different results (type check passes or type check fails) for the same codebase. We created a self-contained reproducible example to demonstrate the problem. This creates 3 projects using project references, where project A depends on project B and that, in turn, depends on project C. There is one TypeScript file in project A, none in B and one in C.
projectA/
, projectB/
and projectC/
.// projectA/A.ts
import C from '../projectC/C';
class A extends C {
constructor() {
super( {} );
this.name.arbitraryFakeMethod();
}
}
// projectA/tsconfig.json
{
"references": [
{
"path": "../projectB"
}
],
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"composite": true,
"outDir": "./dist/",
"incremental": true,
"allowJs": true
},
"include": [
"A.ts"
]
}
// projectB/tsconfig.json
{
"references": [
{
"path": "../projectC"
}
],
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"composite": true,
"allowJs": true,
"outDir": "./dist/",
"incremental": true
}
}
// projectC/C.ts
class C {
name: string;
constructor( options ) {
}
}
export default C;
// projectC/tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"composite": true,
"allowJs": true,
"outDir": "./dist/",
"incremental": true
},
"include": [
"C.ts"
]
}
The clean step is not necessary on your first run, but we include it here in case you want to run through the steps again.
cd projectA
tsc -b --clean
tsc -b
In this case, the expected result is the same as the actual result, which is a build failure with this message:
A.ts:7:15 - error TS2339: Property 'arbitraryFakeMethod' does not exist on type 'string'.
7 this.name.arbitraryFakeMethod();
~~~~~~~~~~~~~~~~~~~
Found 1 error.
In C.ts, change name: string;
to name: any;
tsc -b
Again, the expected result matches the actual result. tsc
succeeds with no output.
In C.ts, change name: any;
back to name: string;
tsc -b
There is no output from tsc
because the type check and build passes successfully. This is incorrect because there is
a type error because string
does not have a method arbitraryFakeMethod
. Note that Step 6 is building the same code as in Step 2. However, in Step 2, the type error is correctly identified, but in Step 6 the type error is missed.
Step 6 should produce the following error (as it correctly did in Step 2):
A.ts:7:15 - error TS2339: Property 'arbitraryFakeMethod' does not exist on type 'string'.
7 this.name.arbitraryFakeMethod();
~~~~~~~~~~~~~~~~~~~
Found 1 error.
Note that changing project A to depend on C directly (not through B) correctly identifies the problem when running tsc -b
in project A, so this bug does require the intermediate transitive dependency. TypeScript correctly caught this error through Version 3.6.0-dev.20190621 (good), but started missing it in 3.6.0-dev.20190622 (bad). For completeness, we will mention that we first detected the problem when projectC was .js code, so it seems to affect both .ts and *.js dependencies.
@jonathanolson and/or @zepumph can one of you please volunteer to review the prior comment, which is a draft issue to report to TypeScript? You should follow the steps, see if you get the same result. Are the instructions clear? Do you have any ideas about the problem, or can you find any paper trail on it in https://github.com/microsoft/TypeScript/issues ? You can check out different versions of typescript like so: npm install typescript@3.6.0-dev.20190622
. You can also see the bug template here: https://github.com/microsoft/TypeScript/issues/new/choose
Also, let me know if you prefer to discuss and work through this synchronously.
The bug report template recommended testing against the nightly build. I ran npm install typescript@next
which gave Version 4.5.0-dev.20210928
. Since I did not install it globally, I used node node_modules/typescript/bin/tsc
instead of just tsc
. But it appeared to have the same problem.
3.0.3 has this problem: 'rootDir' is expected to contain all source files.
and some casing problems.
3.4.5 (version that introduced incremental builds) correctly catches the problem.
3.5.3 correctly catches the problem.
3.6.0-beta fails to catch the problem.
3.6.2 fails to catch the problem.
3.6.4 fails to catch the problem.
So this bug was introduced between 3.5.3 and 3.6.0-beta.
All releases are listed here: https://www.npmjs.com/package/typescript, so we can binary search:
3.6.0-dev.20190530 (first release in that series) - GOOD 3.6.0-dev.20190620 - GOOD 3.6.0-dev.20190704 - BAD 3.6.0-dev.20190627 - BAD 3.6.0-dev.20190623 - BAD 3.6.0-dev.20190621 - GOOD 3.6.0-dev.20190622 - BAD
So the problem was introduced in 3.6.0-dev.20190622. Looking at the TypeScript commits from that day, there are 3-6 commits that look like they relate to project references. There are no referenced issues in the commit messages.
Verified https://github.com/phetsims/chipper/issues/1067#issuecomment-929802925, reproduced the buggy case, directions were quite clear!
I opened the issue in the TypeScript repo, linked above. On hold while we await a response.
In the TypeScript issue, the behavior was identified as a bug, and it was labeled for investigation in TypeScript 4.6. Self-unassigning until then.
We no longer use project references or --build
, closing.
Follow these steps:
(1) in GravityAndOrbitsScene.ts, add a line like:
(2) run tsc -b in gravity-and-orbits, it should fail (3) Then in PhetioObject.js, comment out
this.tandem=DEFAULTS.tandem
(4) run tsc -b in gravity and orbits, it should succeed since tandem now hasany
type. (5) restore PhetioObject.js (6) run tsc -b in gravity and orbits, it should fail like it did in step (2) since all of the code is exactly the same, but it succeeds for unknown reasons. Inspecting chipper/dist/PhetioObject.d.ts, it does have the correct type. So it seems that GravityAndOrbitsScene is not getting triggered to recompile. Also, surprisingly,tsc
succeeds even astsc -b
fails.Running
tsc -b --clean && tsc -b
for each of the 3 compiles correctly shows the missing method. Therefore, it seems there is stale/bad intermediate data from the incremental or project build.