Skip to content

Commit

Permalink
transform-spread: create TS types (not Flow) when using TS (#11378)
Browse files Browse the repository at this point in the history
* βž• add test fixture

* βž• add removeTypeDuplicates for typescript

* βž• add createTSUnionType for typescript

* πŸ’Š fix ConditionalExpression for typescript

* πŸ’Š fix ConditionalExpression

* πŸ’Š fix added test case

* βž• add new line at the end of the file

* πŸ’Š types.every(f) => f(types[0])

* πŸ”„ bug => foo

* βž• add TSBaseType

* βž• add conditions NOT to break backward compatibility
  • Loading branch information
Beraliv committed Apr 15, 2020
1 parent c85aafd commit 6b8f6ab
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 9 deletions.
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);

This comment has been minimized.

Copy link
@TrejGun

TrejGun Apr 16, 2020

@Beraliv shouldn't this be aligned?
like createTSUnionType/createFlowUnionType

This comment has been minimized.

Copy link
@Beraliv

Beraliv Apr 20, 2020

Author Contributor

Yeah, I can easily change that

This comment has been minimized.

Copy link
@Beraliv

Beraliv Apr 20, 2020

Author Contributor

Fixed here #11448

This comment has been minimized.

Copy link
@Beraliv

Beraliv Apr 22, 2020

Author Contributor

βœ… Merged

}

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";

/**
* 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);
}
}
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)) {
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

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" ||
"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

0 comments on commit 6b8f6ab

Please sign in to comment.