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

Add --preserveValueImports #44619

Merged

Conversation

andrewbranch
Copy link
Member

@andrewbranch andrewbranch commented Jun 16, 2021

Fixes #43393
Successor of #44137

Adds a flag --noErasingImportedNames which prevents the elision of unreferenced value-having imports in JS:

// index.ts
import { readFile } from "fs";
eval("console.log(readFile)");

// tsc --module=es2015 💥
eval("console.log(readFile)");
export {};

// tsc --module=es2015 --importsNotUsedAsValues=preserve 💥
import "fs";
eval("console.log(readFile)");

// tsc --module=es2015 --noErasingImportedNames ✅
import { readFile } from "fs";
eval("console.log(readFile)");

It does not prevent the elision of imports that resolve to types or type-only exports, because that would create runtime errors:

// mod.ts
export interface I {}
class C {}
export type { C };

// index.ts
import { I, C } from "./mod";

// tsc --module=es2015
export {};

// tsc --module=es2015 --importsNotUsedAsValues=preserve
import "./mod";

// tsc --module=es2015 --noErasingImportedNames
export {};

However, this means that when combining --noErasingImportedNames with --isolatedModules, types and type-only exports must be imported with import type. Otherwise, single-file transpilation can’t know what import specifiers are safe to preserve:

import { writeFile, WriteFileOptions } from "fs";
//                  ^^^^^^^^^^^^^^^^
// TS1444: 'WriteFileOptions' is a type and must be imported with a type-only import when 'noErasingImportedNames' and 'isolatedModules' are both enabled.

To make this restriction easier to deal with, a subsequent PR will enable making individual import specifiers type-only:

import { writeFile, type WriteFileOptions } from "fs"; // ✅

That PR will also adjust auto-import and organize imports behavior according to these options.

The flag is only valid in es2015+ module emit because downleveling ESM imports to other module systems involves creating a temporary variable with a mangled name and doing property access off that name, so preserving names as written is not possible—the eval example wouldn’t work because the way to access readFile in the JS would be fs_1.readFile (or another subscript if conflicting), not readFile.

@typescript-bot typescript-bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Jun 16, 2021
@andrewbranch andrewbranch changed the title Feature/no erasing imported names Add --noErasingImportedNames Jun 16, 2021
@andrewbranch
Copy link
Member Author

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jun 16, 2021

Heya @andrewbranch, I've started to run the tarball bundle task on this PR at 0b02873. You can monitor the build here.

@andrewbranch andrewbranch marked this pull request as ready for review August 19, 2021 00:35
@andrewbranch
Copy link
Member Author

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Aug 19, 2021

Heya @andrewbranch, I've started to run the tarball bundle task on this PR at bc91a25. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Aug 19, 2021

Hey @andrewbranch, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/108911/artifacts?artifactName=tgz&fileId=4C756A27CDE9FBA8D08572FA701057DC38B850454CF058150C36EFD3FA07C3BD02&fileName=/typescript-4.5.0-insiders.20210819.tgz"
    }
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/pr-build@4.5.0-pr-44619-4".;

@typescript-bot typescript-bot added For Milestone Bug PRs that fix a bug with a specific milestone and removed For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Aug 19, 2021
@andrewbranch andrewbranch changed the title Add --noErasingImportedNames Add --preserveValueImports Aug 26, 2021
@andrewbranch
Copy link
Member Author

Renamed to preserveValueImports

@xiaoxiangmoe
Copy link
Contributor

xiaoxiangmoe commented Aug 30, 2021

import { type WriteFileOptions } from "fs";

Under --preserveValueImports, import "fs"; or import {} from 'fs'; or ;, which one should we compile it to?

@andrewbranch
Copy link
Member Author

My plan was for that to emit import {} from "fs". I think that’s the most intuitive.

@robpalme
Copy link

robpalme commented Sep 2, 2021

import { type WriteFileOptions } from "fs";
My plan was for that to emit import {} from "fs". I think that’s the most intuitive.

That's definitely the desired behavior for "importsNotUsedAsValues": "preserve"

I assume the whole import would be elided in the case of "importsNotUsedAsValues": "remove" (though I really don't mind either way here).

@andrewbranch
Copy link
Member Author

Yeah, my answer was for --preserveValueImports, but I agree with that as well @robpalme.

src/compiler/checker.ts Outdated Show resolved Hide resolved
src/compiler/checker.ts Show resolved Hide resolved
src/compiler/checker.ts Show resolved Hide resolved
src/compiler/checker.ts Show resolved Hide resolved
src/compiler/commandLineParser.ts Outdated Show resolved Hide resolved
src/compiler/diagnosticMessages.json Outdated Show resolved Hide resolved
src/compiler/diagnosticMessages.json Outdated Show resolved Hide resolved
src/compiler/transformers/ts.ts Show resolved Hide resolved
@andrewbranch andrewbranch merged commit 8610ff5 into microsoft:main Sep 8, 2021
@andrewbranch andrewbranch deleted the feature/noErasingImportedNames branch September 8, 2021 23:30
@robpalme
Copy link

robpalme commented Sep 9, 2021

Thank you for landing this feature in time for TypeScript 4.5! 🎉

I am super excited for the follow-on PR to introduce type-only import identifiers. Probably we would wait for that before enabling this feature, to avoid doubling up on import lines (that would occur via the addition of an import type line). IMO this all adds up to excellent progress towards explicitness and simplicity.

@nyngwang
Copy link

import { type WriteFileOptions } from "fs";

Under --preserveValueImports, import "fs"; or import {} from 'fs'; or ;, which one should we compile it to?

@xiaoxiangmoe Sorry for tagging, but I found your example might not be related in that it has nothing to do with the option/flag --preserveValueImports, which is all about value-having imports as clearly stated in OP. To conclude: An import that is used as a type was destined to be removed when
--preserveValueImports was not yet deprecated.

If you found my argument wrong, please let me know. Thanks for your reading.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Milestone Bug PRs that fix a bug with a specific milestone
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

importsNotUsedAsValues: preserve is not preserving
6 participants