Support Flow Mapped Types (#14619)

Co-authored-by: fisker <lionkay@gmail.com>
This commit is contained in:
Jordan Brownandfisker authored and GitHub committed 2023-04-07 09:56:18 +08:00
1 parent 02a50e5bb9
commit f95b0c0296
15 files changed
+454 -48

No files matched your search

+1
View File
@@ -31,6 +31,7 @@ package-lock.json
.node-version
# When installing software on gitpod.io, `core.*` files are generated
/core.*
*.swp
# For 0x flamegraph directory (https://github.com/davidmarkclements/0x)
*.0x/
+12
View File
@@ -0,0 +1,12 @@
#### Support Mapped Types and keyof (#14619 by @jbrown215)
```jsx
// Input
type Mapped = {[key in keyof O]:number};
// Prettier stable
// Does not parse
// Prettier main
type Mapped = { [key in keyof O]: number };
```
+1
View File
@@ -326,6 +326,7 @@
"tldr",
"Tomasek",
"toplevel",
"Tparam",
"Tradeshift",
"Transloadit",
"trippable",
+5 -1
View File
@@ -44,6 +44,7 @@ import {
printDeclareToken,
} from "./misc.js";
import { printTernary } from "./ternary.js";
import { printFlowMappedTypeProperty } from "./mapped-type.js";
function printFlow(path, options, print) {
const { node } = path;
@@ -178,9 +179,12 @@ function printFlow(path, options, print) {
assert.ok(kind === "plus" || kind === "minus");
return kind === "plus" ? "+" : "-";
}
case "KeyofTypeAnnotation":
return ["keyof ", print("argument")];
case "ObjectTypeCallProperty":
return [node.static ? "static " : "", print("value")];
case "ObjectTypeMappedTypeProperty":
return printFlowMappedTypeProperty(path, options, print);
case "ObjectTypeIndexer":
return [
node.static ? "static " : "",
+88
View File
@@ -0,0 +1,88 @@
import { printDanglingComments } from "../../main/comments/print.js";
import hasNewlineInRange from "../../utils/has-newline-in-range.js";
import { locStart, locEnd } from "../loc.js";
import {
group,
softline,
indent,
ifBreak,
line,
} from "../../document/builders.js";
/**
* @param {string | null} optional
* @returns {string}
*/
function printFlowMappedTypeOptionalModifier(optional) {
switch (optional) {
case null:
return "";
case "PlusOptional":
return "+?";
case "MinusOptional":
return "-?";
case "Optional":
return "?";
}
}
function printFlowMappedTypeProperty(path, options, print) {
const { node } = path;
return group([
node.variance ? print("variance") : "",
"[",
indent([print("keyTparam"), " in ", print("sourceType")]),
"]",
printFlowMappedTypeOptionalModifier(node.optional),
": ",
print("propType"),
]);
}
/**
* @param {string} tokenNode
* @param {string} keyword
* @returns {string}
*/
function printTypeScriptMappedTypeModifier(tokenNode, keyword) {
if (tokenNode === "+" || tokenNode === "-") {
return tokenNode + keyword;
}
return keyword;
}
function printTypescriptMappedType(path, options, print) {
const { node } = path;
const shouldBreak = hasNewlineInRange(
options.originalText,
locStart(node),
locEnd(node)
);
return group(
[
"{",
indent([
options.bracketSpacing ? line : softline,
print("typeParameter"),
node.optional
? printTypeScriptMappedTypeModifier(node.optional, "?")
: "",
node.typeAnnotation ? ": " : "",
print("typeAnnotation"),
options.semi ? ifBreak(";") : "",
]),
printDanglingComments(path, options),
options.bracketSpacing ? line : softline,
"}",
],
{ shouldBreak }
);
}
export {
printFlowMappedTypeProperty,
printTypeScriptMappedTypeModifier,
printTypescriptMappedType,
};
+2 -2
View File
@@ -16,7 +16,6 @@ import {
shouldPrintComma,
getFunctionParameters,
isObjectType,
getTypeScriptMappedTypeModifier,
} from "../utils/index.js";
import createGroupIdMapper from "../../utils/create-group-id-mapper.js";
import {
@@ -24,6 +23,7 @@ import {
shouldHugType,
} from "./type-annotation.js";
import { isArrowFunctionVariableDeclarator } from "./assignment.js";
import { printTypeScriptMappedTypeModifier } from "./mapped-type.js";
const getTypeParametersGroupId = createGroupIdMapper("typeParameters");
@@ -119,7 +119,7 @@ function printTypeParameter(path, options, print) {
if (parent.type === "TSMappedType") {
if (parent.readonly) {
parts.push(
getTypeScriptMappedTypeModifier(parent.readonly, "readonly"),
printTypeScriptMappedTypeModifier(parent.readonly, "readonly"),
" "
);
}
+6 -31
View File
@@ -1,8 +1,5 @@
import { printDanglingComments } from "../../main/comments/print.js";
import hasNewlineInRange from "../../utils/has-newline-in-range.js";
import {
join,
line,
softline,
group,
indent,
@@ -12,7 +9,6 @@ import {
import UnexpectedNodeError from "../../utils/unexpected-node-error.js";
import {
isStringLiteral,
getTypeScriptMappedTypeModifier,
shouldPrintComma,
isCallExpression,
isMemberExpression,
@@ -20,7 +16,7 @@ import {
isObjectOrRecordExpression,
} from "../utils/index.js";
import isTsKeywordType from "../utils/is-ts-keyword-type.js";
import { locStart, locEnd } from "../loc.js";
import { locStart } from "../loc.js";
import {
printOptionalToken,
@@ -58,6 +54,7 @@ import {
} from "./type-annotation.js";
import { printEnumDeclaration, printEnumMember } from "./enum.js";
import { printImportKind } from "./module.js";
import { printTypescriptMappedType } from "./mapped-type.js";
function printTypescript(path, options, print) {
const { node } = path;
@@ -239,32 +236,10 @@ function printTypescript(path, options, print) {
case "TSTypeOperator":
return [node.operator, " ", print("typeAnnotation")];
case "TSMappedType": {
const shouldBreak = hasNewlineInRange(
options.originalText,
locStart(node),
locEnd(node)
);
return group(
[
"{",
indent([
options.bracketSpacing ? line : softline,
print("typeParameter"),
node.optional
? getTypeScriptMappedTypeModifier(node.optional, "?")
: "",
node.typeAnnotation ? ": " : "",
print("typeAnnotation"),
ifBreak(semi),
]),
printDanglingComments(path, options),
options.bracketSpacing ? line : softline,
"}",
],
{ shouldBreak }
);
}
case "TSMappedType":
return printTypescriptMappedType(path, options, print);
case "TSMethodSignature": {
const kind = node.kind && node.kind !== "method" ? `${node.kind} ` : "";
parts.push(
@@ -38,6 +38,13 @@ const additionalVisitorKeys = {
],
DeclareEnum: flowVisitorKeys.EnumDeclaration,
InferTypeAnnotation: ["typeParameter"],
KeyofTypeAnnotation: ["argument"],
ObjectTypeMappedTypeProperty: [
"keyTparam",
"propType",
"sourceType",
"variance",
],
QualifiedTypeofIdentifier: ["id", "qualification"],
TupleTypeAnnotation: ["elementTypes"],
TupleTypeSpreadElement: ["label", "typeAnnotation"],
-14
View File
@@ -485,19 +485,6 @@ function isSimpleTemplateLiteral(node) {
});
}
/**
* @param {string} tokenNode
* @param {string} keyword
* @returns {string}
*/
function getTypeScriptMappedTypeModifier(tokenNode, keyword) {
if (tokenNode === "+" || tokenNode === "-") {
return tokenNode + keyword;
}
return keyword;
}
/**
* @param {string} text
* @param {Node} node
@@ -1131,7 +1118,6 @@ export {
hasRestParameter,
getLeftSide,
getLeftSidePathName,
getTypeScriptMappedTypeModifier,
hasLeadingOwnLineComment,
hasNakedLeftSide,
hasNode,
@@ -0,0 +1,253 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`comments.js [babel-flow] format 1`] = `
"Unexpected token, expected "]" (3:7)
1 | type Type = {
2 | // comment
> 3 | +[T in number]: number;
| ^
4 | };
5 |
6 | type Type = {"
`;
exports[`comments.js [babel-ts] format 1`] = `
"Unexpected token (3:3)
1 | type Type = {
2 | // comment
> 3 | +[T in number]: number;
| ^
4 | };
5 |
6 | type Type = {"
`;
exports[`comments.js [typescript] format 1`] = `
"Property or signature expected. (3:3)
1 | type Type = {
2 | // comment
> 3 | +[T in number]: number;
| ^
4 | };
5 |
6 | type Type = {"
`;
exports[`comments.js format 1`] = `
====================================options=====================================
parsers: ["flow", "typescript"]
printWidth: 80
| printWidth
=====================================input======================================
type Type = {
// comment
+[T in number]: number;
};
type Type = {
// comment1
// comment2
+[T in number]: number;
};
type Type = {
// comment
-[T in number]: number;
};
type Type = {
// comment
+ [T in number]: number;
};
type Type = {
// comment
+ [T in number]: number;
};
type Type = {
// comment
+ [T in number]: number;
};
type Type = {
// comment
[T in number]: number;
};
type Type = {
+ // foo
/* bar */ [T in number]: number;
};
=====================================output=====================================
type Type = {
// comment
+[T in number]: number,
};
type Type = {
// comment1
// comment2
+[T in number]: number,
};
type Type = {
// comment
-[T in number]: number,
};
type Type = {
// comment
+[T in number]: number,
};
type Type = {
// comment
+[T in number]: number,
};
type Type = {
// comment
+[T in number]: number,
};
type Type = {
// comment
[T in number]: number,
};
type Type = {
+[/* bar */ T in number]: number, // foo
};
================================================================================
`;
exports[`mapped-types.js [babel-flow] format 1`] = `
"Unexpected token, expected "]" (3:21)
1 | //@flow
2 |
> 3 | type Mapped = {[key in keyof O]: O[key]};
| ^
4 | type MappedLong = {[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]: number};
5 | type MappedWithVariance = {+[key in keyof O]: number};
6 | type MappedWithVarianceLong = {-[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]: number};"
`;
exports[`mapped-types.js [babel-ts] format 1`] = `
"Unexpected token (5:28)
3 | type Mapped = {[key in keyof O]: O[key]};
4 | type MappedLong = {[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]: number};
> 5 | type MappedWithVariance = {+[key in keyof O]: number};
| ^
6 | type MappedWithVarianceLong = {-[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]: number};
7 | type MappedWithPlusOptional = {[key in keyof O]+?: number};
8 | type MappedWithMinusOptional = {[key in keyof O]-?: number};"
`;
exports[`mapped-types.js [typescript] format 1`] = `
"Property or signature expected. (5:28)
3 | type Mapped = {[key in keyof O]: O[key]};
4 | type MappedLong = {[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]: number};
> 5 | type MappedWithVariance = {+[key in keyof O]: number};
| ^
6 | type MappedWithVarianceLong = {-[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]: number};
7 | type MappedWithPlusOptional = {[key in keyof O]+?: number};
8 | type MappedWithMinusOptional = {[key in keyof O]-?: number};"
`;
exports[`mapped-types.js format 1`] = `
====================================options=====================================
parsers: ["flow", "typescript"]
printWidth: 80
| printWidth
=====================================input======================================
//@flow
type Mapped = {[key in keyof O]: O[key]};
type MappedLong = {[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]: number};
type MappedWithVariance = {+[key in keyof O]: number};
type MappedWithVarianceLong = {-[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]: number};
type MappedWithPlusOptional = {[key in keyof O]+?: number};
type MappedWithMinusOptional = {[key in keyof O]-?: number};
type MappedWithOptional = {[key in keyof O]?: number};
type MappedWithPlusOptionalLong = {[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]+?: number};
type MappedWithMinusOptionalLong = {[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]-?: number};
type MappedWithOptionalLong = {[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]?: number};
type MappedWithOptionalAndVariance = {+[key in keyof O]+?: number};
type MappedWithOptionalAndVarianceLong = {+[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]+?: number};
type MappedWithComments = {/*comment about variance */+[key /* comment about key name */ in /* comment before source type */ keyof O /* comment after source type */]/* comment about optionality */? /*comment before colon */:number /*comment about the prop type */};
=====================================output=====================================
//@flow
type Mapped = { [key in keyof O]: O[key] };
type MappedLong = {
[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]: number,
};
type MappedWithVariance = { +[key in keyof O]: number };
type MappedWithVarianceLong = {
-[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]: number,
};
type MappedWithPlusOptional = { [key in keyof O]+?: number };
type MappedWithMinusOptional = { [key in keyof O]-?: number };
type MappedWithOptional = { [key in keyof O]?: number };
type MappedWithPlusOptionalLong = {
[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]+?: number,
};
type MappedWithMinusOptionalLong = {
[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]-?: number,
};
type MappedWithOptionalLong = {
[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]?: number,
};
type MappedWithOptionalAndVariance = { +[key in keyof O]+?: number };
type MappedWithOptionalAndVarianceLong = {
+[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]+?: number,
};
type MappedWithComments = {
/*comment about variance */ +[key /* comment about key name */ in /* comment before source type */ keyof O /* comment after source type */ /* comment about optionality */ /*comment before colon */]?: number /*comment about the prop type */,
};
================================================================================
`;
exports[`ts-compatibility.js [babel-flow] format 1`] = `
"Unexpected token, expected "]" (3:19)
1 | // Tests that on single-line mapped types Flow and TS make the same formatting decisions
2 |
> 3 | type Test = {[key in T]: number};
| ^
4 | type Test = {[key in keyof T]: number};
5 | type Test = {[otherKeyName in keyof T]: number};
6 | type Test = {[key in T]:number};"
`;
exports[`ts-compatibility.js format 1`] = `
====================================options=====================================
parsers: ["flow", "typescript"]
printWidth: 80
| printWidth
=====================================input======================================
// Tests that on single-line mapped types Flow and TS make the same formatting decisions
type Test = {[key in T]: number};
type Test = {[key in keyof T]: number};
type Test = {[otherKeyName in keyof T]: number};
type Test = {[key in T]:number};
type Test = {[key in T]+?:number};
type Test = {[key in T]-?: number};
=====================================output=====================================
// Tests that on single-line mapped types Flow and TS make the same formatting decisions
type Test = { [key in T]: number };
type Test = { [key in keyof T]: number };
type Test = { [otherKeyName in keyof T]: number };
type Test = { [key in T]: number };
type Test = { [key in T]+?: number };
type Test = { [key in T]-?: number };
================================================================================
`;
@@ -0,0 +1,40 @@
type Type = {
// comment
+[T in number]: number;
};
type Type = {
// comment1
// comment2
+[T in number]: number;
};
type Type = {
// comment
-[T in number]: number;
};
type Type = {
// comment
+ [T in number]: number;
};
type Type = {
// comment
+ [T in number]: number;
};
type Type = {
// comment
+ [T in number]: number;
};
type Type = {
// comment
[T in number]: number;
};
type Type = {
+ // foo
/* bar */ [T in number]: number;
};
@@ -0,0 +1,7 @@
run_spec(import.meta, ["flow", "typescript"], {
errors: {
"babel-flow": ["comments.js", "mapped-types.js", "ts-compatibility.js"],
"babel-ts": ["comments.js", "mapped-types.js"],
typescript: ["comments.js", "mapped-types.js"],
},
});
@@ -0,0 +1,15 @@
//@flow
type Mapped = {[key in keyof O]: O[key]};
type MappedLong = {[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]: number};
type MappedWithVariance = {+[key in keyof O]: number};
type MappedWithVarianceLong = {-[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]: number};
type MappedWithPlusOptional = {[key in keyof O]+?: number};
type MappedWithMinusOptional = {[key in keyof O]-?: number};
type MappedWithOptional = {[key in keyof O]?: number};
type MappedWithPlusOptionalLong = {[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]+?: number};
type MappedWithMinusOptionalLong = {[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]-?: number};
type MappedWithOptionalLong = {[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]?: number};
type MappedWithOptionalAndVariance = {+[key in keyof O]+?: number};
type MappedWithOptionalAndVarianceLong = {+[key in keyof AReallyLongNameThatShouldReallyMostDefinitelyCauseALineWrap]+?: number};
type MappedWithComments = {/*comment about variance */+[key /* comment about key name */ in /* comment before source type */ keyof O /* comment after source type */]/* comment about optionality */? /*comment before colon */:number /*comment about the prop type */};
@@ -0,0 +1,8 @@
// Tests that on single-line mapped types Flow and TS make the same formatting decisions
type Test = {[key in T]: number};
type Test = {[key in keyof T]: number};
type Test = {[otherKeyName in keyof T]: number};
type Test = {[key in T]:number};
type Test = {[key in T]+?:number};
type Test = {[key in T]-?: number};
@@ -446,6 +446,9 @@ exports[`visitor keys estree 1`] = `
"JsonRoot": [
"node",
],
"KeyofTypeAnnotation": [
"argument",
],
"LabeledStatement": [
"label",
"body",
@@ -563,6 +566,12 @@ exports[`visitor keys estree 1`] = `
"static",
"method",
],
"ObjectTypeMappedTypeProperty": [
"keyTparam",
"propType",
"sourceType",
"variance",
],
"ObjectTypeProperty": [
"key",
"value",