Inline | null and | void (#1734)

TypeScript doesn't have the concept of `?` for nullable options and instead you have to write `| null` and `| void`. This is annoying to have it use the long form, so we're now inlining them.

While working on this, I found out a few issues with the way we deal with those:
- We only align objects if the parent is a union. This means that if you have `Array<{ object }>`, the object is not aligned properly. The fix is to move the alignment logic to the union, and not the child.
- When doing so, it messes up with the comment alignment, so we have to manually handle children comment printing in the union code.

It doesn't yet fix #1727 because the hardcoded type names are different, i'll follow up in a PR.
master
Christopher Chedeau 2017-05-25 16:26:40 -07:00 committed by GitHub
parent 62679f20f0
commit fe49650700
4 changed files with 99 additions and 17 deletions

View File

@ -817,7 +817,7 @@ function genericPrintNoParens(path, options, print, args) {
const leftBrace = n.exact ? "{|" : "{";
const rightBrace = n.exact ? "|}" : "}";
const parent = path.getParentNode(0);
const parentIsUnionTypeAnnotation = parent.type === "UnionTypeAnnotation";
let propertiesField;
if (n.type === "TSTypeLiteral") {
@ -881,10 +881,7 @@ function genericPrintNoParens(path, options, print, args) {
content = concat([
leftBrace,
indent(
align(
parentIsUnionTypeAnnotation ? 2 : 0,
concat([options.bracketSpacing ? line : softline, concat(props)])
)
concat([options.bracketSpacing ? line : softline, concat(props)])
),
ifBreak(
canHaveTrailingSeparator &&
@ -892,10 +889,7 @@ function genericPrintNoParens(path, options, print, args) {
? separator
: ""
),
align(
parentIsUnionTypeAnnotation ? 2 : 0,
concat([options.bracketSpacing ? line : softline, rightBrace])
),
concat([options.bracketSpacing ? line : softline, rightBrace]),
n.typeAnnotation ? ": " : "",
path.call(print, "typeAnnotation")
]);
@ -910,7 +904,7 @@ function genericPrintNoParens(path, options, print, args) {
parent &&
shouldHugArguments(parent) &&
parent.params[0] === n) ||
(n.type === "ObjectTypeAnnotation" &&
(shouldHugType(n) &&
parentParentParent &&
shouldHugArguments(parentParentParent) &&
parentParentParent.params[0].typeAnnotation.typeAnnotation === n)
@ -1994,10 +1988,31 @@ function genericPrintNoParens(path, options, print, args) {
!(parent.type === "TypeAlias" &&
hasLeadingOwnLineComment(options.originalText, n));
//const token = isIntersection ? "&" : "|";
// {
// a: string
// } | null | void
// should be inlined and not be printed in the multi-line variant
const shouldHug = shouldHugType(n);
// We want to align the children but without its comment, so it looks like
// | child1
// // comment
// | child2
const printed = path.map(typePath => {
let printedType = typePath.call(print);
if (!shouldHug && shouldIndent) {
printedType = align(2, printedType);
}
return comments.printComments(typePath, () => printedType, options);
}, "types");
if (shouldHug) {
return join(" | ", printed);
}
const code = concat([
ifBreak(concat([shouldIndent ? line : "", "| "])),
join(concat([line, "| "]), path.map(print, "types"))
join(concat([line, "| "]), printed)
]);
return group(shouldIndent ? indent(code) : code);
@ -3341,7 +3356,7 @@ function printTypeParameters(path, options, print, paramsKey) {
const shouldInline =
n[paramsKey].length === 1 &&
(n[paramsKey][0].type === "ObjectTypeAnnotation" ||
(shouldHugType(n[paramsKey][0]) ||
n[paramsKey][0].type === "NullableTypeAnnotation");
if (shouldInline) {
@ -4479,6 +4494,25 @@ function isNodeStartingWithDeclare(node, options) {
);
}
function shouldHugType(node) {
if (node.type === "ObjectTypeAnnotation") {
return true;
}
if (node.type === "UnionTypeAnnotation") {
const count = node.types.filter(
n =>
n.type === "VoidTypeAnnotation" ||
n.type === "NullLiteralTypeAnnotation"
).length;
if (node.types.length - 1 === count) {
return true;
}
}
return false;
}
function shouldHugArguments(fun) {
return (
fun &&
@ -4489,10 +4523,9 @@ function shouldHugArguments(fun) {
(fun.params[0].type === "Identifier" &&
fun.params[0].typeAnnotation &&
fun.params[0].typeAnnotation.type === "TypeAnnotation" &&
fun.params[0].typeAnnotation.typeAnnotation.type ===
"ObjectTypeAnnotation") ||
shouldHugType(fun.params[0].typeAnnotation.typeAnnotation)) ||
(fun.params[0].type === "FunctionTypeParam" &&
fun.params[0].typeAnnotation.type === "ObjectTypeAnnotation")) &&
shouldHugType(fun.params[0].typeAnnotation))) &&
!fun.rest
);
}
@ -4557,8 +4590,13 @@ function printAstToDoc(ast, options, addAlignmentSize) {
function printGenerically(path, args) {
const node = path.getValue();
const parent = path.getParentNode(0);
// We let JSXElement print its comments itself because it adds () around
if (node && node.type === "JSXElement") {
// UnionTypeAnnotation has to align the child without the comments
if (
(node && node.type === "JSXElement") ||
(parent && parent.type === "UnionTypeAnnotation")
) {
return genericPrint(path, options, printGenerically, args);
}

View File

@ -0,0 +1,32 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`union.js 1`] = `
interface RelayProps {
articles: Array<{
__id: string,
} | null> | null | void | 1,
}
interface RelayProps {
articles: Array<{
__id: string,
} | null> | null | void,
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
interface RelayProps {
articles:
| Array<{
__id: string
} | null>
| null
| void
| 1
}
interface RelayProps {
articles: Array<{
__id: string
} | null> | null | void
}
`;

View File

@ -0,0 +1 @@
run_spec(__dirname, null, ["babylon"]);

11
tests/flow_union/union.js Normal file
View File

@ -0,0 +1,11 @@
interface RelayProps {
articles: Array<{
__id: string,
} | null> | null | void | 1,
}
interface RelayProps {
articles: Array<{
__id: string,
} | null> | null | void,
}