Skip to content

Commit

Permalink
fix: support some TSTypes in inferrer
Browse files Browse the repository at this point in the history
  • Loading branch information
JLHwung committed Jun 10, 2022
1 parent 34bd5cd commit 0a1c8e2
Show file tree
Hide file tree
Showing 29 changed files with 313 additions and 107 deletions.
1 change: 1 addition & 0 deletions packages/babel-plugin-transform-for-of/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ export default declare((api, options: Options) => {
if (
right.isArrayExpression() ||
right.isGenericType("Array") ||
// todo(Babel 8): the following line can be removed as it is supported by isGenericType("Array")
t.isArrayTypeAnnotation(right.getTypeAnnotation())
) {
path.replaceWith(_ForOfStatementArray(path));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"plugins": ["transform-for-of", "transform-flow-strip-types"]
"plugins": ["transform-for-of"],
"presets": ["typescript"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// This won't be optimize because when `for-of` is handled, the b's type annotation has been removed by the Flow plugin
function a(b: Array<any>) {
for (const y of b) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": ["transform-for-of", "transform-flow-strip-types"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This won't be optimize because when `for-of` is handled, the b's type annotation has been removed by the Flow plugin
function a(b) {
var _iterator = babelHelpers.createForOfIteratorHelper(b),
_step;

try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
const y = _step.value;
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
for (const y of (b: Array<any>)) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": ["transform-for-of", "transform-flow-strip-types"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
for (var _i = 0, _arr = b; _i < _arr.length; _i++) {
const y = _arr[_i];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// This won't be optimize because when `for-of` is handled, the b's type annotation has been removed by the TS plugin
function a(b: Array<any>) {
for (const y of b) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"plugins": ["transform-for-of"],
"presets": ["typescript"]
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
for (var _i = 0, _arr = b; _i < _arr.length; _i++) {
const y = _arr[_i];
}

// This won't be optimize because when `for-of` is handled, the b's type annotation has been removed by the TS plugin
function a(b) {
var _iterator = babelHelpers.createForOfIteratorHelper(b),
_step;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
for (const y of (b as Array<any>)) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"plugins": ["transform-for-of"],
"presets": ["typescript"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
for (var _i = 0, _arr = b; _i < _arr.length; _i++) {
const y = _arr[_i];
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
function foo() {
const x = 1 ? a() : b();
const y = a() || b();

return [...x, ...y];
}

function a(): Array<number> {
return [];
}

function b(): Array<number> {
return [];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"plugins": ["transform-spread"],
"presets": ["flow"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
function foo() {
const x = 1 ? a() : b();
const y = a() || b();
return [].concat(x, y);
}

function a() {
return [];
}

function b() {
return [];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
function foo() {
const x = 1 ? a() : b();
const y = a() || b();

return [...x, ...y];
}

function a(): Array<number> {
return [];
}

function b(): Array<number> {
return [];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
function foo() {
const x = 1 ? a() : b();
const y = a() || b();
return [].concat(x, y);
}

function a() {
return [];
}

function b() {
return [];
}
29 changes: 26 additions & 3 deletions packages/babel-traverse/src/path/inference/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as inferers from "./inferers";
import {
anyTypeAnnotation,
isAnyTypeAnnotation,
isArrayTypeAnnotation,
isBooleanTypeAnnotation,
isEmptyTypeAnnotation,
isFlowBaseAnnotation,
Expand All @@ -11,6 +12,10 @@ import {
isMixedTypeAnnotation,
isNumberTypeAnnotation,
isStringTypeAnnotation,
isTSArrayType,
isTSTypeAnnotation,
isTSTypeReference,
isTupleTypeAnnotation,
isTypeAnnotation,
isUnionTypeAnnotation,
isVoidTypeAnnotation,
Expand All @@ -29,7 +34,9 @@ export function getTypeAnnotation(this: NodePath): t.FlowType {
return type;
}
type = this._getTypeAnnotation() || anyTypeAnnotation();
if (isTypeAnnotation(type)) type = type.typeAnnotation;
if (isTypeAnnotation(type) || isTSTypeAnnotation(type)) {
type = type.typeAnnotation;
}
this.setData("typeAnnotation", type);
return type;
}
Expand Down Expand Up @@ -158,8 +165,24 @@ export function baseTypeStrictlyMatches(

export function isGenericType(this: NodePath, genericName: string): boolean {
const type = this.getTypeAnnotation();
if (genericName === "Array") {
// T[]
if (
isTSArrayType(type) ||
isArrayTypeAnnotation(type) ||
isTupleTypeAnnotation(type)
) {
return true;
}
}
return (
isGenericTypeAnnotation(type) &&
isIdentifier(type.id, { name: genericName })
(isGenericTypeAnnotation(type) &&
isIdentifier(type.id, {
name: genericName,
})) ||
(isTSTypeReference(type) &&
isIdentifier(type.typeName, {
name: genericName,
}))
);
}
32 changes: 4 additions & 28 deletions packages/babel-traverse/src/path/inference/inferer-reference.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import type NodePath from "../index";
import {
BOOLEAN_NUMBER_BINARY_OPERATORS,
createFlowUnionType,
createTSUnionType,
createTypeAnnotationBasedOnTypeof,
createUnionTypeAnnotation,
isTSTypeAnnotation,
numberTypeAnnotation,
voidTypeAnnotation,
} from "@babel/types";
import type * as t from "@babel/types";
import type Binding from "../../scope/binding";

import { createUnionType } from "./util";

export default function (this: NodePath, node: any) {
if (!this.isReferenced()) return;

Expand Down Expand Up @@ -106,15 +104,7 @@ function getTypeAnnotationBindingConstantViolations(binding, path, name) {
return;
}

if (isTSTypeAnnotation(types[0]) && createTSUnionType) {
return createTSUnionType(types);
}

if (createFlowUnionType) {
return createFlowUnionType(types);
}

return createUnionTypeAnnotation(types);
return createUnionType(types);
}

function getConstantViolationsBefore(binding: Binding, path, functions?) {
Expand Down Expand Up @@ -231,22 +221,8 @@ function getConditionalAnnotation<T extends t.Node>(
}

if (types.length) {
if (isTSTypeAnnotation(types[0]) && createTSUnionType) {
return {
typeAnnotation: createTSUnionType(types),
ifStatement,
};
}

if (createFlowUnionType) {
return {
typeAnnotation: createFlowUnionType(types),
ifStatement,
};
}

return {
typeAnnotation: createUnionTypeAnnotation(types),
typeAnnotation: createUnionType(types),
ifStatement,
};
}
Expand Down
69 changes: 22 additions & 47 deletions packages/babel-traverse/src/path/inference/inferers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,26 @@ import {
arrayTypeAnnotation,
booleanTypeAnnotation,
buildMatchMemberExpression,
createFlowUnionType,
createTSUnionType,
createUnionTypeAnnotation,
genericTypeAnnotation,
identifier,
isTSTypeAnnotation,
nullLiteralTypeAnnotation,
numberTypeAnnotation,
stringTypeAnnotation,
tupleTypeAnnotation,
unionTypeAnnotation,
voidTypeAnnotation,
isIdentifier,
} from "@babel/types";
import type * as t from "@babel/types";

export { default as Identifier } from "./inferer-reference";

import { createUnionType } from "./util";
import type NodePath from "..";

export function VariableDeclarator(this: NodePath<t.VariableDeclarator>) {
const id = this.get("id");

if (!id.isIdentifier()) return;
const init = this.get("init");

let type = init.getTypeAnnotation();

if (type?.type === "AnyTypeAnnotation") {
// Detect "var foo = Array()" calls so we can optimize for arrays vs iterables.
if (
init.isCallExpression() &&
init.get("callee").isIdentifier({ name: "Array" }) &&
!init.scope.hasBinding("Array", true /* noGlobals */)
) {
type = ArrayExpression();
}
}

return type;
if (!this.get("id").isIdentifier()) return;
return this.get("init").getTypeAnnotation();
}

export function TypeCastExpression(node) {
Expand All @@ -61,6 +42,10 @@ export function TSAsExpression(node) {

TSAsExpression.validParent = true;

export function TSNonNullExpression() {
return this.get("expression").getTypeAnnotation();
}

export function NewExpression(this: NodePath<t.NewExpression>, node) {
if (this.get("callee").isIdentifier()) {
// only resolve identifier callee
Expand Down Expand Up @@ -119,16 +104,7 @@ export function LogicalExpression(this: NodePath<t.LogicalExpression>) {
this.get("right").getTypeAnnotation(),
];

if (isTSTypeAnnotation(argumentTypes[0]) && createTSUnionType) {
// @ts-expect-error Fixme: getTypeAnnotation also returns TS types
return createTSUnionType(argumentTypes);
}

if (createFlowUnionType) {
return createFlowUnionType(argumentTypes);
}

return createUnionTypeAnnotation(argumentTypes);
return createUnionType(argumentTypes);
}

export function ConditionalExpression(this: NodePath<t.ConditionalExpression>) {
Expand All @@ -137,16 +113,7 @@ export function ConditionalExpression(this: NodePath<t.ConditionalExpression>) {
this.get("alternate").getTypeAnnotation(),
];

if (isTSTypeAnnotation(argumentTypes[0]) && createTSUnionType) {
// @ts-expect-error Fixme: getTypeAnnotation also returns TS types
return createTSUnionType(argumentTypes);
}

if (createFlowUnionType) {
return createFlowUnionType(argumentTypes);
}

return createUnionTypeAnnotation(argumentTypes);
return createUnionType(argumentTypes);
}

export function SequenceExpression(this: NodePath<t.SequenceExpression>) {
Expand Down Expand Up @@ -224,7 +191,12 @@ export function CallExpression(this: NodePath<t.CallExpression>) {
const { callee } = this.node;
if (isObjectKeys(callee)) {
return arrayTypeAnnotation(stringTypeAnnotation());
} else if (isArrayFrom(callee) || isObjectValues(callee)) {
} else if (
isArrayFrom(callee) ||
isObjectValues(callee) ||
// Detect "var foo = Array()" calls so we can optimize for arrays vs iterables.
isIdentifier(callee, { name: "Array" })
) {
return arrayTypeAnnotation(anyTypeAnnotation());
} else if (isObjectEntries(callee)) {
return arrayTypeAnnotation(
Expand All @@ -245,14 +217,17 @@ function resolveCall(callee) {
callee = callee.resolve();

if (callee.isFunction()) {
if (callee.is("async")) {
if (callee.is("generator")) {
const { node } = callee;
if (node.async) {
if (node.generator) {
return genericTypeAnnotation(identifier("AsyncIterator"));
} else {
return genericTypeAnnotation(identifier("Promise"));
}
} else {
if (callee.node.returnType) {
if (node.generator) {
return genericTypeAnnotation(identifier("Iterator"));
} else if (callee.node.returnType) {
return callee.node.returnType;
} else {
// todo: get union type of all return arguments
Expand Down

0 comments on commit 0a1c8e2

Please sign in to comment.