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

API to work with type definitions in plugins #7162

Open
ark120202 opened this Issue Jan 6, 2018 · 2 comments

Comments

Projects
None yet
3 participants
@ark120202

ark120202 commented Jan 6, 2018

Choose one: is this a bug report or feature request?

In Babel 7 we'll get a TypeScript support in Babylon. It's really awesome, but now, since babel just parses it, there is no way to get type of a variable, like TypeChecker.getTypeAtLocation in TypeScript Compiler API. There are typeAnnotation and typeParameters properties on some nodes, but they exist only on annotated ones. It's understandable why Babel won't check TypeScript in babel-preset-typescript by default, but it's not really about checking (althrough it can be an option, because we have to spawn TS checker anyway). Probably it's possible to use TS checker within plugins, so it won't require any changes in Babel, but having one official way would be easier to use, will reduce chance of multiple checkers used in one compilation (two plugins from different developers have to use different checkers, so 2x time for parsing and checking) and provide a way to use type checking without binding it only to TS (even vanilla JS can have types with TS definitions).

Type Sources

These things can be used as type sources:

  1. TypeScript (including type definitions)
  2. Flow
  3. JSDoc

All these ways are already implemented in editors, like VS Code, so it should be possible to implement them in Babel

Use cases

function foo(a: number) {
  if (typeof a === 'number') {
    console.log(a);
  }
  return 'removed-' + a;
}

foo(1);
foo(2);

can be minified to

function foo(a) {
  console.log(a);
  // There is no need to generate another if branch,
  // since we are sure that typeof a === 'number' is true
}

foo(1);
foo(2);

Google Closure Compiler already has this optimization implemented, but having it in babel-minify would be more conconvenient to use and won't require things like typescript-closure-compiler

  • Also, it can help me with development of babel-lua (for now it's just proof of concept and another use of new generatorOverride API, but with types it can become actually working thing)

API

Something like scope.getType(path.node) is expected for plugins.

The thing I leave for discussion is that all type sources have different format of definitions. Possible solutions are:

  1. Leave types as-is and pass source along with them, so plugin developers have to support what they want. We also can return all found types, so, if project has TS and JSDoc, plugins can access both of them and use one they implemented.
{
  "source": "typescript",
  "type": "string | null | undefined"
}
  1. Convert all formats to one (nearly impossible because of difference between Flow and TypeScript types).
@babel-bot

This comment has been minimized.

Show comment
Hide comment
@babel-bot

babel-bot Jan 6, 2018

Collaborator

Hey @ark120202! We really appreciate you taking the time to report an issue. The collaborators
on this project attempt to help as many people as possible, but we're a limited number of volunteers,
so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack
community that typically always has someone willing to help. You can sign-up here
for an invite.

Collaborator

babel-bot commented Jan 6, 2018

Hey @ark120202! We really appreciate you taking the time to report an issue. The collaborators
on this project attempt to help as many people as possible, but we're a limited number of volunteers,
so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack
community that typically always has someone willing to help. You can sign-up here
for an invite.

@simonbuchan

This comment has been minimized.

Show comment
Hide comment
@simonbuchan

simonbuchan Apr 12, 2018

Note that editors use the language service to request types, they don't reimplement this.

Not sure babel would want to support cross-file information? e.g.:

// a.ts
import { getB } from "./b";
export function getA() { return "Bob"; }
function useB() {
  const b = getB(); // 'b' is number
}

// b.ts
import { getA } from "./a";
export function getB() { return 123; }
function useA() {
  const a = getA(); // 'a' is string
}

It's not clear what this kind of thing would look like with the current babel API, which is very much only operating on single files at a time (sort of excluding using generator with an AST built from multiple sources).

The way this works in typescript, for example, is you register a host implementation that tells TS how to find source files, and map imports to source files, and it operates on a program as a whole.

But lets say that's we can figure that out later, is this feasible?

TS and Flow have far more different binding behaviors than just about anything else, but if this were e.g. plugin-typescript-type-binding, as part of preset-typescript, that is still theoretically managable.

The TS code for this is fairly large but not completely unreasonable to re-implement, but getting it to initially match typescript behavior and keeping it up to date with changes would be far more difficult that just following the current parsing / generating. Importantly, TS has breaking changes in how it binds and checks types in nearly every minor release, you would probably need something like plugin-typescript-2.7-type-binding!

For me I wouldn't be too comfortable with using user-provided type info for dead-code elimination, it's simply too easy to get wrong - for code you can fix, having an unreachable code error would be more useful, and it's probably relevant that TS chose not do this! That said, there definitely are uses for this I would want: documentation generation, emitting type declarations for bundles, and support for transpilation features that require type info, such as const enum and namespace.

simonbuchan commented Apr 12, 2018

Note that editors use the language service to request types, they don't reimplement this.

Not sure babel would want to support cross-file information? e.g.:

// a.ts
import { getB } from "./b";
export function getA() { return "Bob"; }
function useB() {
  const b = getB(); // 'b' is number
}

// b.ts
import { getA } from "./a";
export function getB() { return 123; }
function useA() {
  const a = getA(); // 'a' is string
}

It's not clear what this kind of thing would look like with the current babel API, which is very much only operating on single files at a time (sort of excluding using generator with an AST built from multiple sources).

The way this works in typescript, for example, is you register a host implementation that tells TS how to find source files, and map imports to source files, and it operates on a program as a whole.

But lets say that's we can figure that out later, is this feasible?

TS and Flow have far more different binding behaviors than just about anything else, but if this were e.g. plugin-typescript-type-binding, as part of preset-typescript, that is still theoretically managable.

The TS code for this is fairly large but not completely unreasonable to re-implement, but getting it to initially match typescript behavior and keeping it up to date with changes would be far more difficult that just following the current parsing / generating. Importantly, TS has breaking changes in how it binds and checks types in nearly every minor release, you would probably need something like plugin-typescript-2.7-type-binding!

For me I wouldn't be too comfortable with using user-provided type info for dead-code elimination, it's simply too easy to get wrong - for code you can fix, having an unreachable code error would be more useful, and it's probably relevant that TS chose not do this! That said, there definitely are uses for this I would want: documentation generation, emitting type declarations for bundles, and support for transpilation features that require type info, such as const enum and namespace.

@ark120202 ark120202 referenced this issue Sep 16, 2018

Closed

Status #1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment