@@ -14,6 +14,12 @@ describe("type syntax", function () {
1414 parser : require ( "flow-parser" ) ,
1515 } ;
1616
17+ function checkEquiv ( a : string , b : string ) {
18+ const aAst = parse ( a , flowParserParseOptions ) ;
19+ const bAst = parse ( b , flowParserParseOptions ) ;
20+ types . astNodesAreEquivalent . assert ( aAst , bAst ) ;
21+ }
22+
1723 function check ( source : string , parseOptions ?: any ) {
1824 parseOptions = parseOptions || flowParserParseOptions ;
1925 const ast1 = parse ( source , parseOptions ) ;
@@ -212,12 +218,6 @@ describe("type syntax", function () {
212218 check ( "type H = (Obj?.['bar'])[string][];" ) ;
213219 check ( "type I = Obj?.['bar']?.[string][];" ) ;
214220
215- function checkEquiv ( a : string , b : string ) {
216- const aAst = parse ( a , flowParserParseOptions ) ;
217- const bAst = parse ( b , flowParserParseOptions ) ;
218- types . astNodesAreEquivalent . assert ( aAst , bAst ) ;
219- }
220-
221221 // Since FastPath#needsParens does not currently add any parentheses to
222222 // these expressions, make sure they do not matter for parsing the AST.
223223
@@ -231,4 +231,108 @@ describe("type syntax", function () {
231231 "type F = Obj['bar']?.[string][];" ,
232232 ) ;
233233 } ) ;
234+
235+ it ( "parenthesizes correctly" , ( ) => {
236+ // The basic binary operators `&` and `|`.
237+ // `&` binds tighter than `|`
238+ check ( "type Num = number & (empty | mixed);" ) ; // parens needed
239+ check ( "type Num = number | empty & mixed;" ) ; // equivalent to `…|(…&…)`
240+
241+ // Unary suffix `[]`, with the above.
242+ // `[]` binds tighter than `&` or `|`
243+ check ( "type T = (number | string)[];" ) ;
244+ check ( "type T = number | string[];" ) ; // a union
245+ check ( "type T = (number & mixed)[];" ) ;
246+ check ( "type T = number & mixed[];" ) ; // an intersection
247+
248+ // Unary prefix `?`, with the above.
249+ // `?` binds tighter than `&` or `|`
250+ check ( "type T = ?(A & B);" ) ;
251+ check ( "type T = ?(A | B);" ) ;
252+ // `?` binds less tightly than `[]`
253+ check ( "type T = (?number)[];" ) ; // array of nullable
254+ check ( "type T = ?number[];" ) ; // nullable of array
255+
256+ // (Optional) indexed-access types, with the above.
257+ // `[…]` and `?.[…]` bind (their left) tighter than either `&` or `|`
258+ check ( "type T = (O & P)['x'];" ) ;
259+ check ( "type T = (O | P)['x'];" ) ;
260+ check ( "type T = (O & P)?.['x'];" ) ;
261+ check ( "type T = (O | P)?.['x'];" ) ;
262+ // `[…]` and `?.[…]` bind (their left) tighter than `?`
263+ check ( "type T = (?O)['x'];" ) ; // indexed-access of nullable
264+ check ( "type T = ?O['x'];" ) ; // nullable of indexed-access
265+ check ( "type T = (?O)?.['x'];" ) ; // optional-indexed-access of nullable
266+ check ( "type T = ?O?.['x'];" ) ; // nullable of optional-indexed-access
267+ // `[…]` and `?.[…]` provide brackets on their right, so skip parens:
268+ check ( "type T = A[B & C];" ) ;
269+ check ( "type T = A[B | C];" ) ;
270+ check ( "type T = A[?B];" ) ;
271+ check ( "type T = A[B[]];" ) ;
272+ check ( "type T = A[B[C]];" ) ;
273+ check ( "type T = A[B?.[C]];" ) ;
274+ check ( "type T = A?.[B & C];" ) ;
275+ check ( "type T = A?.[B | C];" ) ;
276+ check ( "type T = A?.[?B];" ) ;
277+ check ( "type T = A?.[B[]];" ) ;
278+ check ( "type T = A?.[B[C]];" ) ;
279+ check ( "type T = A?.[B?.[C]];" ) ;
280+ // `[…]` and `?.[…]` interact in a nonobvious way:
281+ // OptionalIndexedAccessType inside IndexedAccessType.
282+ check ( "type T = (O?.['x']['y'])['z'];" ) ; // indexed of optional-indexed
283+ check ( "type T = O?.['x']['y']['z'];" ) ; // optional-indexed throughout
284+
285+ return ;
286+ // Skip test cases involving function types, because those are currently
287+ // broken in other ways. Those will be fixed by:
288+ // https://github.com/benjamn/recast/pull/1089
289+
290+ // Function types.
291+ // Function binds less tightly than binary operators at right:
292+ check ( "type T = (() => number) & O;" ) ; // an intersection
293+ check ( "type T = (() => number) | void;" ) ; // a union
294+ check ( "type T = () => number | void;" ) ; // a function
295+ check ( "type T = (() => void)['x'];" ) ;
296+ check ( "type T = () => void['x'];" ) ; // a function
297+ check ( "type T = (() => void)?.['x'];" ) ;
298+ check ( "type T = () => void?.['x'];" ) ; // a function
299+ // … and less tightly than suffix operator:
300+ check ( "type T = (() => void)[];" ) ; // an array
301+ check ( "type T = () => void[];" ) ; // a function
302+
303+ // Function does bind tighter than prefix operator (how could it not?)
304+ checkEquiv ( "type T = ?() => void;" , "type T = ?(() => void);" ) ;
305+ // … and tighter than `&` or `|` at left (ditto):
306+ checkEquiv ( "type T = A | () => void;" , "type T = A | (() => void);" ) ;
307+ checkEquiv ( "type T = A & () => void;" , "type T = A & (() => void);" ) ;
308+ // … but we choose to insert parens anyway:
309+ check ( "type T = ?(() => void);" ) ;
310+ check ( "type T = A | (() => void);" ) ;
311+ check ( "type T = A & (() => void);" ) ;
312+ // We don't insert parens for the *right* operand of indexed access,
313+ // though, that'd be silly (sillier than writing such a type at all?):
314+ check ( "type T = A[() => void];" ) ;
315+ check ( "type T = A?.[() => void];" ) ;
316+
317+ // Here's one reason we insert those parens we don't strictly have to:
318+ // Even when the parent is something at left so that function binds
319+ // tighter than it, *its* parent (or further ancestor) might be
320+ // something at right that binds tighter than function.
321+ // E.g., union of nullable of function:
322+ check ( "type T = ?(() => void) | A;" ) ;
323+ checkEquiv ( "type T = ?() => void | A;" , "type T = ?() => (void | A);" ) ;
324+ // … or intersection of nullable of function:
325+ check ( "type T = ?(() => void) & A;" ) ;
326+ checkEquiv ( "type T = ?() => void & A;" , "type T = ?() => (void & A);" ) ;
327+ // … or array or (optional-)indexed-access of nullable of function:
328+ check ( "type T = ?(() => void)[];" ) ;
329+ check ( "type T = ?(() => void)['x'];" ) ;
330+ check ( "type T = ?(() => void)?.['x'];" ) ;
331+ // … or union of intersection:
332+ check ( "type T = A & (() => void) | B;" ) ;
333+ // Or for an example beyond the grandparent: union of cubic nullable:
334+ check ( "type T = ???(() => void) | B;" ) ;
335+ // … or union of intersection of nullable:
336+ check ( "type T = A & ?(() => void) | B;" ) ;
337+ } ) ;
234338} ) ;
0 commit comments