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

Request: compiler option to disable elision of "unused" imports #18586

Closed
jpap opened this issue Sep 20, 2017 · 9 comments
Closed

Request: compiler option to disable elision of "unused" imports #18586

jpap opened this issue Sep 20, 2017 · 9 comments
Labels
Duplicate An existing issue was already created

Comments

@jpap
Copy link

jpap commented Sep 20, 2017

I'd love to see a compiler option that allows us to disable the elision of unused imports.

I am using a Babel plugin that translates the pug into React createElement statements, so I can use the compact/readable pug language instead of JSX. The babel plugin runs after the typescript compiler.

As shown in the code (app.ts) below, I import a React component (component.ts) using an import statement. The babel plugin will convert a pug-style JSX declaration into the React.createElement call as appropriate. However the imported components are no-longer visible as typescript has dropped the associated imports because it did not see them being used.

Incorporating a compiler option to disable this "optimization" is desired.

TypeScript Version: 2.5.2

Code

The @types/babel-plugin-react-pug package is a prerequisite for the code below.

[component.ts]
A standard React component exported as Component.

import * as React from "react";

export
class Component extends React.Component {
  public render() {
    // The babel-plugin-react-pug babel plugin converts this 
    // to React.createElement("p", null, "Hello world from Component.")
    return pug`
      p Hello world from Component.
    `;
  }
}

[app.ts]
A React app/container that imports the above component and includes it as a child.

import * as React from "react";

// This does not work because typescript removes the import
//import { Component } from "component";

// Workaround: force typescript to keep the import, without renaming "Component"
import { Component as _Component } from "component";
const Component = _Component;

class App extends React.Component {
  public render() {
    // The babel-plugin-react-pug babel plugin converts this
    // to React.createElement(Component, null)
    return pug`
      Component
    `;
  }
}

Expected behavior:

The workaround given above should be replaced by a compiler option to disable the import elision; the import code above then reduces to the regular (simple) import statement:

import { Component } from "component";

Secondly, the typescript compiler should not rename the Component symbol. (I have noticed that sometimes, but not always, the typescript compiler will rename imported symbols.)

Actual behavior:

The typescript compiler removes the import statement, so the Component symbol is not emitted into the compiled JavaScript.

After babel runs the babel-plugin-react-pug plugin, and replaces the pug source with React.createElement(Component, null), JavaScript declares that it cannot find the symbol Component (because typescript dropped it).

@mhegazy
Copy link
Contributor

mhegazy commented Sep 21, 2017

Not eliding imports can result in importing non-existing models. for similar situations with React, we made it so that React import will not be elided if you have at least one JSX element. obviously this is not the case with pug, but i do not think stopping all elisions (especially in absence of an import type ... syntax) is the right way to go.

Have you tried the new TS-babel plugin (https://www.npmjs.com/package/babel-plugin-transform-typescript)? it should be to get you the desired behavior and has the benefit of one emit pipeline to handle.

@jpap
Copy link
Author

jpap commented Sep 21, 2017

Can you elaborate on what you mean by non-existing models?

Thanks for the suggestion on TS-babel -- unfortunately it looks like it's very alpha and simply strips out types and does not do any type-checking. In a build pipeline that would still require the use of the TypeScript compiler for type-checking, awkwardly bifurcating it into two passes.

Would you consider having additional syntax to selectively disable import eliding? Something like the following?

import { Component } from "component" always;

@mhegazy
Copy link
Contributor

mhegazy commented Sep 21, 2017

it's very alpha and simply strips out types and does not do any type-checking. In a build pipeline that would still require the use of the TypeScript compiler for type-checking, awkwardly bifurcating it into two passes.

The intention here is not to replace TS, it is only for emit. your type checking still has to happen. should not be too different from what you are doing today having babel pass over the generated .js files.

Would you consider having additional syntax to selectively disable import eliding? Something like the following?

you can find relevant conversations in #2812

@mhegazy
Copy link
Contributor

mhegazy commented Sep 21, 2017

on any rate, this request is already tracked by #9191

@mhegazy mhegazy added the Duplicate An existing issue was already created label Sep 21, 2017
@jpap
Copy link
Author

jpap commented Sep 21, 2017

The intention here is not to replace TS, it is only for emit. your type checking still has to happen. should not be too different from what you are doing today having babel pass over the generated .js files.

That's right: you have to awkwardly bifurcate the build into two passes: 1) type checking, 2) transpilation. Currently it is one simple pipeline from TS to Babel that is easily achieved with chained Webpack loaders.

you can find relevant conversations in #2812

I had read through it previously but the issue appears closed/abandoned (2016).

on any rate, this request is already tracked by #9191

The suggested approach there,

import "component";
import { Component } from "component";

still results in Component being elided on TS 2.5.2.

Given that many developers have stumbled on this issue for a number of years, I do hope you're able to give it fresh consideration with a simple extension to the language, a compiler option, or source-local compiler directive.

@aluanhaddad
Copy link
Contributor

aluanhaddad commented Sep 21, 2017

In a build pipeline that would still require the use of the TypeScript compiler for type-checking, awkwardly bifurcating it into two passes.

I actually find having transpilation and typechecking decoupled to be a better workflow. It improves performance during the edit-save-debug cycle. I find this to be true even if you're only using TypeScript. I use one instance for typechecking (say integrated with VS Code via a problem matcher) and another instance for transpilation (say hosted inside of loader/bundler).

@jpap
Copy link
Author

jpap commented Sep 21, 2017

I actually find having transpilation and typechecking decoupled to be a better workflow. It improves performance during the edit-save-debug cycle. I find this to be true even if you're only using TypeScript. I use one instance for typechecking (say integrated with VS Code via a problem matcher) and another instance for transpilation (say hosted inside of loader/bundler).

We do this now -- "online" type checking is done in an IDE (e.g. VS Code), however you must absolutely also have type checking part of a build system otherwise you will eventually shoot yourself in the foot.

Your suggestion unfortunately and unnecessarily complicates the build process in order to compensate for a quirk in the language. Who is to say that the babel plugin above starts to elide imports in the future, in order to approach further parity with the TS compiler?

Again, I would say that many developers have been tripped up on this unexpected language feature and it would be nice to have an elegant way in which to get back the elided imports where required, and without hacky workarounds as outlined earlier. I hope you're able to give it further consideration. :D

@aluanhaddad
Copy link
Contributor

aluanhaddad commented Oct 2, 2017

@jpap

however you must absolutely also have type checking part of a build system otherwise you will eventually shoot yourself in the foot.

Totally agree, but I don't like the --noEmitOnError approach that a lot of tools have taken to. They change the default behavior of the language and confuse users as to its purpose.

Again, I would say that many developers have been tripped up on this unexpected language feature and it would be nice to have an elegant way in which to get back the elided imports where required, and without hacky workarounds as outlined earlier.

I was just presenting my viewpoint. I a program only works in the presence of an import that is subject to elision then there is an implicit dependency that should probably be exposed.

I hope you're able to give it further consideration. :D

I just don't think it is a good idea at this point so I thought I would provide a counterargument. It is up to the TypeScript team to determine if they want to revisit this design decision.

@mhegazy
Copy link
Contributor

mhegazy commented Oct 17, 2017

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@mhegazy mhegazy closed this as completed Oct 17, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants