-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Used import elided from emit where source is JavaScript #48588
Comments
I don’t know why TS is eliding the import (that sounds like a bug), but you could try enabling |
My assumption is that this may be related to incorrect type signature. I would expect to see |
So TruffleContract might have written its types badly, but this is working JavaScript code that the TypeScript compiler breaks on copy. TypeScript should not be breaking that, and partial conversion to TypeScript should not require rewriting dependency libraries' types, especially when copying JavaScript seems so simple for TS to handle! |
@fatcerberus thanks for the intended-helpful suggestion to try enabling |
TS must remove all type imports. Not sure if namespace with only type export inside can be treated type-only, but there are no valid access to that namespace to use it value position. In your case you need to fill issue against package to fix bundled declarations. declare module '@truffle/contract' {
// or better types instead of unknown
export default function(json?: unknown): unknown {}
} |
I disagree: That may be true in TypeScript files but there should be no sense of "removing type imports" in non-TS files that it's just copying through. This helps avoid the status quo situation where introducing TypeScript in one part of a program means rewriting valid JavaScript in different parts of the program and/or having to retype dependency libraries, which is a whole lot of work. For anybody else who encounters this issue, the workaround is adding a post-TypeScript build step to copy the JavaScript file as-is from the source directory to the target directory. I still believe this is an issue in TypeScript which should be fixed in TypeScript, to have TypeScript do that copying by itself without an override. |
You seem to be under the impression that TS is supposed to be simply copying |
@fatcerberus The documentation does give the impression that's what it's doing. Certainly there is no suggestion in there that the valid JS files would have to be rewritten or changed in any way to avoid having TypeScript break them. |
From #44619 that implemented it (initial post wasn't updated with other updates from comments, however this logic still holds)
While I agree that eliding imports in JS "without notice" is unexpected, on the other hand with info available to TS via built-in package declarations (only types in namespace) it's possible that this will lead to runtime error, but without Either way (whether this will or won't be changed in TS) type declarations in should be fixed, otherwise that will be elided again when |
I agree that TruffleContract types should be fixed and accept that a problem will exist when the
This import is NOT a type-only import! The compiler should be able to recognize that (a) it's being used as a value, whether or not it thinks that's correct, and (b) type-only imports don't happen in non-TS JS files! The compiler is already doing the check for (a) and incorrectly concludes that it's not being used; that could be fixed. However, implementing (b) would be more robust and might be much simpler. The compiler already knows which files are JS files that it's skipping checks on; it could also skip elision of whatever it thinks might be a type-only import. |
There's possibly a crazy design aspect here, but I would never have thought that we would drop a default export used as a value. It seems like an unmistakable bug that we would consult the But instead, import Yadda from "@truffle/contract";
const blah = Yadda;
initContract({}, fs, Yadda); is producing the following code const blah = Yadda;
initContract({}, fs, Yadda);
export {}; Thoughts @andrewbranch? |
My recollection of stuff getting marked for preservation is that it’s based on usage, not the target symbol’s declaration space, but given the behavior you’re describing, you must be right that it’s going off the .d.ts and thinking it can’t be a value. I agree this should be changed; usage is a more reliable indicator. I will say that it’s a slightly unusual use case to rely on tsc’s JS-to-JS emit while simultaneously ignoring its check errors, which would be telling you that This does bring up kind of an interesting tangent about what we think it means for you to import types, in plain old ESM imports, in JS files. I guess there is nothing stopping you from doing this: // @Filename: types.ts
export interface Foo {} // @Filename: index.js
import { Foo } from "./types"
/** @param {Foo} foo */
function takeFoo(foo) {} We will happily process this without errors, and there’s like, technically nothing wrong with it since we elide the import declaration, but now that I’m thinking about it, this feels really weird. Like it’s very TypeScripty to casually import types in import declarations. Because so much JS is checked with |
In principle I’m not opposed to saying let’s never do any import elision from JS files, but then we’ll find out how many people are relying on this behavior on purpose 😬 |
Even more strange is that in a plain JS file we don't seem to elide the import. |
huh? I thought this whole issue was about imports getting elided in plain JS files… |
So I think the playground is overriding |
Well, even that is an oversimplification. With With |
It's elided at least for No |
Still curious what @DanielRosenwasser thinks about the general case of using ESM imports in JS files to import types. Worth opening another issue? |
Looks like someone really awesome filed an issue about that back in 2018 #22159 |
It might be OK to have some difference in handling based on whether When checkJs is false, I expect TypeScript to just be passing plain JS files, including imports, through as-is, no matter how many errors TS thinks the code has. There are many false positive flags raised by TS and many cases where perfectly valid JS has to be rewritten/changed as part of a conversion to We should really not have to be seeing 4-digit commit counts or weeks-long efforts for a single PR to be able to get things back into a stable working state from attempting to convert one file without hacky overrides. |
So I attempted to convert that file to TS and hit up against this issue again today, with the value import still being elided. |
We don’t close stale bugs; we auto-close questions / external issues / working as intended, and occasionally we manually close suggestions that we don’t think we’ll take. This is a bug, and it has a milestone, and an assignee, and was discussed at a design meeting recently, and I already fixed the related #22159, so it’s quite active. |
Bug Report
🔎 Search Terms
import elided javascript used detect unused 6133
🕗 Version & Regression Information
⏯ Playground Link
Playground link with relevant code
💻 Code
Suppose the following JavaScript calls a function defined in TypeScript:
caller.js:
In functions.ts:
A subset of tsConfig.js:
package.json has
"type":"module"
needed for other project requirements, sorequire
is not available. Changing the import syntax toimport { TruffleContract } from '@truffle/contract';
causes TypeScript to maintain the import line but there is an (expected) runtimeThat again requires manually re-inserting the import statement because TypeScript drops the line falsely asserting that it's not used even when it's used in an expression in the very next line, and it's undefined at runtime because the original syntax is the correct one to use.
🙁 Actual behavior
The TruffleContract import is dropped from the JavaScript file as part of copying that into the target location, leading to a runtime error that TruffleContract is not defined in the expression where it is used. Manually re-adding it to the copied JavaScript file after every transpilation avoids the runtime error.
🙂 Expected behavior
The documentation for allowJs does not give any indication that the TypeScript transpiler will break existing JavaScript, instead of just copying it through. Instead, it says:
The FAQ states that TypeScript "removes module imports that aren't used in any expression." This one is, so by the reasoning in the FAQ, it isn't being elided, but in practice, it is, breaking what used to be running code.
The workaround discussed there and here is to include a general import line, such as
import '@truffle/contract';
and while this does make it through to the target location, it doesn't overcome the runtime error.require
is not available for use but the documentation indicates a single use of what's imported should suffice and there is one here.Overall, the expected behavior is that the JavaScript file is copied directly with no alterations, and certainly no breaking changes.
More specifically, the expected behavior is that an import which is actually used (in this case, it is used for passing a module implementation as a parameter) is not removed. In this case, TypeScript appears to be wrongly detecting that the import is unused (ts(6133)), as the 'unused import' formatting/warning shows up in the editor as well. This incorrect 'unused' status detection even triggers when the imported module is used in e.g. a console.log statement on the very next line, by itself or with the
typeof
operator.The implementation is not imported and used directly in functions.ts because different sources of implementations are used at runtime depending on factors which do not appear relevant to a minimal example. This is not in Angular.
The text was updated successfully, but these errors were encountered: