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 (t.isTSTypeAnnotation(types[0]) && t.createTSUnionType) {
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 (t.isTSTypeAnnotation(types[0]) && t.createTSUnionType) {
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 (t.isTSTypeAnnotation(argumentTypes[0]) && t.createTSUnionType) {
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 (t.isTSTypeAnnotation(argumentTypes[0]) && t.createTSUnionType) {
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 foo() {
const x = 1 ? a() : b();

return [...x];
}

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

function b(): number[] {
return [];
}
@@ -0,0 +1,4 @@
{
"plugins": ["transform-spread"],
"presets": ["typescript"]
}
3 changes: 3 additions & 0 deletions packages/babel-types/src/asserts/generated/index.js
Expand Up @@ -1202,6 +1202,9 @@ export function assertTSTypeElement(node: Object, opts?: Object = {}): void {
export function assertTSType(node: Object, opts?: Object = {}): void {
assert("TSType", node, opts);
}
export function assertTSBaseType(node: Object, opts?: Object = {}): void {
assert("TSBaseType", node, opts);
}
export function assertNumberLiteral(node: Object, opts: Object): void {
console.trace(
"The node type NumberLiteral has been renamed to NumericLiteral",
Expand Down
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/constants/generated/index.js
Expand Up @@ -51,3 +51,4 @@ export const JSX_TYPES = FLIPPED_ALIAS_KEYS["JSX"];
export const PRIVATE_TYPES = FLIPPED_ALIAS_KEYS["Private"];
export const TSTYPEELEMENT_TYPES = FLIPPED_ALIAS_KEYS["TSTypeElement"];
export const TSTYPE_TYPES = FLIPPED_ALIAS_KEYS["TSType"];
export const TSBASETYPE_TYPES = FLIPPED_ALIAS_KEYS["TSBaseType"];
6 changes: 3 additions & 3 deletions packages/babel-types/src/definitions/typescript.js
Expand Up @@ -143,14 +143,14 @@ const tsKeywordTypes = [

for (const type of tsKeywordTypes) {
defineType(type, {
aliases: ["TSType"],
aliases: ["TSType", "TSBaseType"],
visitor: [],
fields: {},
});
}

defineType("TSThisType", {
aliases: ["TSType"],
aliases: ["TSType", "TSBaseType"],
visitor: [],
fields: {},
});
Expand Down Expand Up @@ -300,7 +300,7 @@ defineType("TSMappedType", {
});

defineType("TSLiteralType", {
aliases: ["TSType"],
aliases: ["TSType", "TSBaseType"],
visitor: ["literal"],
fields: {
literal: validateType([
Expand Down
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,65 @@
import {
isTSAnyKeyword,
isTSUnionType,
isTSBaseType,
} 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];
}

// Analogue of FlowBaseAnnotation
if (isTSBaseType(node)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

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;
}
30 changes: 30 additions & 0 deletions packages/babel-types/src/validators/generated/index.js
Expand Up @@ -4611,6 +4611,36 @@ export function isTSType(node: ?Object, opts?: Object): boolean {

return false;
}
export function isTSBaseType(node: ?Object, opts?: Object): boolean {
if (!node) return false;

const nodeType = node.type;
if (
nodeType === "TSBaseType" ||
Copy link
Contributor Author

Choose a reason for hiding this comment

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

"TSAnyKeyword" === nodeType ||
"TSBooleanKeyword" === nodeType ||
"TSBigIntKeyword" === nodeType ||
"TSNeverKeyword" === nodeType ||
"TSNullKeyword" === nodeType ||
"TSNumberKeyword" === nodeType ||
"TSObjectKeyword" === nodeType ||
"TSStringKeyword" === nodeType ||
"TSSymbolKeyword" === nodeType ||
"TSUndefinedKeyword" === nodeType ||
"TSUnknownKeyword" === nodeType ||
"TSVoidKeyword" === nodeType ||
"TSThisType" === nodeType ||
"TSLiteralType" === nodeType
) {
if (typeof opts === "undefined") {
return true;
} else {
return shallowEqual(node, opts);
}
}

return false;
}
export function isNumberLiteral(node: ?Object, opts?: Object): boolean {
console.trace(
"The node type NumberLiteral has been renamed to NumericLiteral",
Expand Down