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

*  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
This commit is contained in:
beraliv 2020-04-15 22:47:28 +03:00 committed by GitHub
parent c85aafdff6
commit 6b8f6ab2de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 170 additions and 9 deletions

View File

@ -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);
}
return t.createUnionTypeAnnotation(types);
}
function getConstantViolationsBefore(binding, path, functions) {
@ -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,

View File

@ -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() {

View File

@ -0,0 +1,13 @@
function foo() {
const x = 1 ? a() : b();
return [...x];
}
function a(): number[] {
return [];
}
function b(): number[] {
return [];
}

View File

@ -0,0 +1,4 @@
{
"plugins": ["transform-spread"],
"presets": ["typescript"]
}

View File

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

View File

@ -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);
}
}

View File

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

View File

@ -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: {},
});
@ -300,7 +300,7 @@ defineType("TSMappedType", {
});
defineType("TSLiteralType", {
aliases: ["TSType"],
aliases: ["TSType", "TSBaseType"],
visitor: ["literal"],
fields: {
literal: validateType([

View File

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

View File

@ -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;
}

View File

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