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

`import type` in TypeScript #10981

Open
DanielRosenwasser opened this issue Jan 11, 2020 · 20 comments
Open

`import type` in TypeScript #10981

DanielRosenwasser opened this issue Jan 11, 2020 · 20 comments
Assignees

Comments

@DanielRosenwasser
Copy link
Member

@DanielRosenwasser DanielRosenwasser commented Jan 11, 2020

TypeScript 3.8 will support a new import type construct. While there's already some logic for similar stuff (Flow has its own import type), TypeScript's has a few extra restrictions.

  • import type can only be used on imports with named imports or with a default import - but not both.

    import type FooDefault, { Bar, Baz } from "module"; // error!
  • Even though import type creates no emit, an import type functionally shadows variables from the outer scope, and using these declarations in a value position that will be emitted causes an error.

    import type { RegExp } from "special-regexp-module";
    
    // Okay - RegExp won't be used at runtime here.
    declare class Foo extends RegExp { }
    
    // Okay - RegExp won't be used at runtime here.
    let r: typeof RegExp;
    
    // Error! RegExp is imported via import type and cannot be used as a value here.
    new RegExp()

See more at

@DanielRosenwasser

This comment has been minimized.

Copy link
Member Author

@DanielRosenwasser DanielRosenwasser commented Jan 11, 2020

CC @andrewbranch

@kaicataldo

This comment has been minimized.

Copy link
Member

@kaicataldo kaicataldo commented Jan 11, 2020

For my own understanding, how do we decide what the interface for AST nodes for TypeScript-specific syntax is like? Is there a spec?

@andrewbranch

This comment has been minimized.

Copy link

@andrewbranch andrewbranch commented Jan 11, 2020

If it helps, here’s the changes to TypeScript’s parse tree node interfaces: https://github.com/microsoft/TypeScript/pull/35200/files#diff-4b8bd1eea29904f1be39cd864e1a45c0

The only change was adding a boolean isTypeOnly property to ImportClause and ExportDeclaration.

@armano2

This comment has been minimized.

Copy link
Contributor

@armano2 armano2 commented Jan 12, 2020

For my own understanding, how do we decide what the interface for AST nodes for TypeScript-specific syntax is like? Is there a spec?

i have same issue, i was planning to add this to @typescript-eslint but i'm unsure if we want to extend existing ImportDeclaration by adding new property to it or creating new node type TSImportTypeDeclaration

@JLHwung JLHwung self-assigned this Jan 14, 2020
@nicolo-ribaudo

This comment has been minimized.

Copy link
Member

@nicolo-ribaudo nicolo-ribaudo commented Jan 14, 2020

Currently, we use a .type property for flow imports. It would be nice to keep them aligned.

@armano2

This comment has been minimized.

Copy link
Contributor

@armano2 armano2 commented Jan 14, 2020

type property seems good to me, for imports/exports that are not type do we want to set is as null, false or undefined?

@JLHwung JLHwung assigned kaicataldo and unassigned JLHwung Jan 14, 2020
@jridgewell

This comment has been minimized.

Copy link
Member

@jridgewell jridgewell commented Jan 14, 2020

Currently, we use a .type property for flow imports. It would be nice to keep them aligned.

The explorer suggests we use importKind: 'type' for type imports and importKind: 'value' for value imports.

@nicolo-ribaudo

This comment has been minimized.

Copy link
Member

@nicolo-ribaudo nicolo-ribaudo commented Jan 14, 2020

Oh, you are right 🤦‍♂
And we are using exportKind for export type { x } from "x"/export { x } from "x".

Note that we are already inconsistent between out TS and Flow implementations, because we also set exportKind for export type foo = bar when parsing flow.

@MLoughry

This comment has been minimized.

Copy link

@MLoughry MLoughry commented Jan 16, 2020

I don't suppose anyone's working on a code mod to migrate existing code to this syntax? 🤞

@andrewbranch

This comment has been minimized.

Copy link

@andrewbranch andrewbranch commented Jan 16, 2020

@MLoughry if you set importsNotUsedAsValues: "error" in your tsconfig, I plan to have a quick fix for the resulting errors. It shouldn’t be too hard to write a small program to do the same across a whole project, but that’s not something tsc or the language service currently supports.

@nicolo-ribaudo

This comment has been minimized.

Copy link
Member

@nicolo-ribaudo nicolo-ribaudo commented Jan 16, 2020

Also, this will be hard to implement using Babel because we don't have type information.

@MLoughry

This comment has been minimized.

Copy link

@MLoughry MLoughry commented Jan 16, 2020

@andrewbranch Is there a way to programmatically invoke quick fixes across a set of files? Our project is over 20k modules, and tsserver can't really hold the entire project in memory at once. 😅

@andrewbranch

This comment has been minimized.

Copy link

@andrewbranch andrewbranch commented Jan 16, 2020

Sort of. There’s a language service command to get code fixes for a given file, which will return text change range information that you should be able to pump into a String.prototype.splice on the string contents of a file and write it back to disk.

@jridgewell

This comment has been minimized.

Copy link
Member

@jridgewell jridgewell commented Jan 16, 2020

Also, this will be hard to implement using Babel because we don't have type information.

Actually, I think it'll be really easy for imports. We don't have type information, but we do know whether an imported value is only used in type positions in the AST. The difficult one is for exports, since we have no idea if an export { Foo } from "foo.js" is exporting a value or a type.

@nicolo-ribaudo

This comment has been minimized.

Copy link
Member

@nicolo-ribaudo nicolo-ribaudo commented Jan 16, 2020

Oh you are right.
And if someone is already using Babel, export { Foo } from "foo.js" will already be broken for them when Foo is a type: we can then assume that it is a value.

@andrewbranch

This comment has been minimized.

Copy link

@andrewbranch andrewbranch commented Jan 16, 2020

Note that the assumption may not hold up for any re-exported import, not just export declaration re-exports:

import { Foo } from "foo.js"

function f(p: Foo) {}

// `Foo` was only used as a type *here*, but we still don’t know whether
// it may have a value meaning accessed by another module.
export { Foo };
@jridgewell

This comment has been minimized.

Copy link
Member

@jridgewell jridgewell commented Jan 16, 2020

Note that the assumption may not hold up for any re-exported import, not just export declaration re-exports:

Yup. We'll have trouble with any re-exported thing. But my intuition is that re-exporting is much less common than using imported values, or only exporting local values.

@ExE-Boss

This comment has been minimized.

Copy link
Contributor

@ExE-Boss ExE-Boss commented Jan 17, 2020

Will it be possible to import types from @types/* packages directly?

Example:

import type Foo from "@types/foo";
import type { Bar } from "@types/bar";

This would be particularly useful for importing types from non‑npm @types/* packages.

@nicolo-ribaudo

This comment has been minimized.

Copy link
Member

@nicolo-ribaudo nicolo-ribaudo commented Jan 17, 2020

I don't see how it's different from importing from any other package, from a Babel point of view.

@ExE-Boss

This comment has been minimized.

Copy link
Contributor

@ExE-Boss ExE-Boss commented Jan 17, 2020

I thought this was the TypeScript repository when I posted my previous comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
9 participants
You can’t perform that action at this time.