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

transform-spread: create TS types (not Flow) when using TS #11378

Merged
merged 11 commits into from Apr 15, 2020
17 changes: 15 additions & 2 deletions packages/babel-traverse/src/path/inference/inferer-reference.js
Expand Up @@ -91,9 +91,15 @@ function getTypeAnnotationBindingConstantViolations(binding, path, name) {
}
}

if (types.length) {
return t.createUnionTypeAnnotation(types);
if (!types.length) {
return;
}

if (types.every(t.isTSTypeAnnotation)) {
Beraliv marked this conversation as resolved.
Show resolved Hide resolved
return t.createTSUnionType(types);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is technically a breaking change: it makes the next @babel/traverse version incompatible with @babel/types <= 7.9.5, because it doesn't contain this function.

We should use t.createTSUnionType?.(types) instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this is true, thanks for mentioning that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolo-ribaudo it will change the behaviour as returns UnionTypeAnnotation and now it returns undefined

I added if-check in 5e9c920

}

return t.createUnionTypeAnnotation(types);
}

function getConstantViolationsBefore(binding, path, functions) {
Expand Down Expand Up @@ -201,6 +207,13 @@ function getConditionalAnnotation(binding, path, name) {
}

if (types.length) {
if (types.every(t.isTSTypeAnnotation)) {
return {
typeAnnotation: t.createTSUnionType(types),
ifStatement,
};
}

return {
typeAnnotation: t.createUnionTypeAnnotation(types),
ifStatement,
Expand Down
20 changes: 16 additions & 4 deletions packages/babel-traverse/src/path/inference/inferers.js
Expand Up @@ -83,17 +83,29 @@ export function BinaryExpression(node) {
}

export function LogicalExpression() {
return t.createUnionTypeAnnotation([
const argumentTypes = [
this.get("left").getTypeAnnotation(),
this.get("right").getTypeAnnotation(),
]);
];

if (argumentTypes.every(t.isTSTypeAnnotation)) {
return t.createTSUnionType(argumentTypes);
}

return t.createUnionTypeAnnotation(argumentTypes);
}

export function ConditionalExpression() {
return t.createUnionTypeAnnotation([
const argumentTypes = [
this.get("consequent").getTypeAnnotation(),
this.get("alternate").getTypeAnnotation(),
]);
];

if (argumentTypes.every(t.isTSTypeAnnotation)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return t.createTSUnionType(argumentTypes);
}

return t.createUnionTypeAnnotation(argumentTypes);
}

export function SequenceExpression() {
Expand Down
13 changes: 13 additions & 0 deletions packages/babel-traverse/test/fixtures/type-reference/input.ts
@@ -0,0 +1,13 @@
function bug() {
Beraliv marked this conversation as resolved.
Show resolved Hide resolved
const x = 1 ? a() : b();

return [...x];
}

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

function b(): number[] {
return [];
}
@@ -0,0 +1,4 @@
{
"plugins": ["transform-spread"],
"presets": ["typescript"]
}
19 changes: 19 additions & 0 deletions packages/babel-types/src/builders/typescript/createTSUnionType.js
@@ -0,0 +1,19 @@
import { TSUnionType } from "../generated";
import removeTypeDuplicates from "../../modifications/typescript/removeTypeDuplicates";
Copy link
Contributor Author

@Beraliv Beraliv Apr 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


/**
* Takes an array of `types` and flattens them, removing duplicates and
* returns a `UnionTypeAnnotation` node containg them.
*/
export default function createTSUnionType(
typeAnnotations: Array<Object>,
): Object {
const types = typeAnnotations.map(type => type.typeAnnotations);
const flattened = removeTypeDuplicates(types);

if (flattened.length === 1) {
return flattened[0];
} else {
return TSUnionType(flattened);
Beraliv marked this conversation as resolved.
Show resolved Hide resolved
}
}
1 change: 1 addition & 0 deletions packages/babel-types/src/index.js
Expand Up @@ -10,6 +10,7 @@ export * from "./asserts/generated";
// builders
export { default as createTypeAnnotationBasedOnTypeof } from "./builders/flow/createTypeAnnotationBasedOnTypeof";
export { default as createUnionTypeAnnotation } from "./builders/flow/createUnionTypeAnnotation";
export { default as createTSUnionType } from "./builders/typescript/createTSUnionType";
export * from "./builders/generated";

// clone
Expand Down
@@ -0,0 +1,89 @@
import {
isTSAnyKeyword,
isTSUnionType,
isTSBooleanKeyword,
isTSBigIntKeyword,
isTSNeverKeyword,
isTSNullKeyword,
isTSNumberKeyword,
isTSObjectKeyword,
isTSStringKeyword,
isTSUnknownKeyword,
isTSVoidKeyword,
isTSUndefinedKeyword,
isTSLiteralType,
isTSThisType,
} from "../../validators/generated";

/**
* Dedupe type annotations.
*/
export default function removeTypeDuplicates(
nodes: Array<Object>,
): Array<Object> {
const generics = {};
const bases = {};

// store union type groups to circular references
const typeGroups = [];

const types = [];

for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (!node) continue;

// detect duplicates
if (types.indexOf(node) >= 0) {
continue;
}

// this type matches anything
if (isTSAnyKeyword(node.type)) {
return [node];
}

// Basic TS types, analogue of FlowBaseAnnotation
if (
Beraliv marked this conversation as resolved.
Show resolved Hide resolved
isTSBooleanKeyword(node) ||
isTSBigIntKeyword(node) ||
isTSNeverKeyword(node) ||
isTSNullKeyword(node) ||
isTSNumberKeyword(node) ||
isTSObjectKeyword(node) ||
isTSStringKeyword(node) ||
isTSUndefinedKeyword(node) ||
isTSUnknownKeyword(node) ||
isTSVoidKeyword(node) ||
isTSThisType(node) ||
isTSLiteralType(node)
) {
bases[node.type] = node;
continue;
}

if (isTSUnionType(node)) {
if (typeGroups.indexOf(node.types) < 0) {
nodes = nodes.concat(node.types);
typeGroups.push(node.types);
}
continue;
}

// TODO: add generic types
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ I need clarification here

First I tried TSTypeParameter e0f3fc2#diff-31b76153974cd0302007f05a570dfde6R49

Now I'm sure it does NOT cover all cases


types.push(node);
}

// add back in bases
for (const type of Object.keys(bases)) {
types.push(bases[type]);
}

// add back in generics
for (const name of Object.keys(generics)) {
types.push(generics[name]);
}

return types;
}