@@ -14,6 +14,14 @@ describe("Flow type syntax", function () {
1414 parser : require ( "flow-parser" ) ,
1515 } ;
1616
17+ function checkEquiv ( a : string , b : string ) {
18+ it ( `handles equivalently \`${ a } \` vs. \`${ b } \`` , ( ) => {
19+ const aAst = parse ( a , flowParserParseOptions ) ;
20+ const bAst = parse ( b , flowParserParseOptions ) ;
21+ types . astNodesAreEquivalent . assert ( aAst , bAst ) ;
22+ } ) ;
23+ }
24+
1725 function check ( source : string , parseOptions ?: any ) {
1826 it ( `handles: ${ source } ` , ( ) => {
1927 parseOptions = parseOptions || flowParserParseOptions ;
@@ -25,14 +33,6 @@ describe("Flow type syntax", function () {
2533 } ) ;
2634 }
2735
28- function checkEquiv ( a : string , b : string ) {
29- it ( `handles equivalently \`${ a } \` vs. \`${ b } \`` , ( ) => {
30- const aAst = parse ( a , flowParserParseOptions ) ;
31- const bAst = parse ( b , flowParserParseOptions ) ;
32- types . astNodesAreEquivalent . assert ( aAst , bAst ) ;
33- } ) ;
34- }
35-
3636 describe ( "should parse and print type annotations correctly" , function ( ) {
3737 // Import type annotations
3838 check ( "import type foo from 'foo';" ) ;
@@ -276,6 +276,8 @@ describe("Flow type syntax", function () {
276276 check ( "type B = Array<string>?.[number];" ) ;
277277 check ( "type C = Obj?.['bar']['baz'];" ) ;
278278 check ( "type D = (Obj?.['bar'])['baz'];" ) ;
279+ check ( "type C3 = Obj?.['foo']['bar']['baz'];" ) ;
280+ check ( "type D3 = (Obj?.['foo']['bar'])['baz'];" ) ;
279281 check ( "type E = Obj?.['bar'][];" ) ;
280282 check ( "type F = Obj?.['bar'][boolean][];" ) ;
281283 check ( "type G = Obj['bar']?.[boolean][];" ) ;
@@ -284,15 +286,112 @@ describe("Flow type syntax", function () {
284286
285287 // Since FastPath#needsParens does not currently add any parentheses to
286288 // these expressions, make sure they do not matter for parsing the AST.
287-
288289 checkEquiv (
289290 "type F = (Obj?.['bar'])?.[string][];" ,
290291 "type F = Obj?.['bar']?.[string][];" ,
291292 ) ;
292-
293293 checkEquiv (
294294 "type F = (Obj['bar'])?.[string][];" ,
295295 "type F = Obj['bar']?.[string][];" ,
296296 ) ;
297297 } ) ;
298+
299+ describe ( "parenthesizes correctly" , ( ) => {
300+ // The basic binary operators `&` and `|`.
301+ // `&` binds tighter than `|`
302+ check ( "type Num = number & (empty | mixed);" ) ; // parens needed
303+ check ( "type Num = number | empty & mixed;" ) ; // equivalent to `…|(…&…)`
304+
305+ // Unary suffix `[]`, with the above.
306+ // `[]` binds tighter than `&` or `|`
307+ check ( "type T = (number | string)[];" ) ;
308+ check ( "type T = number | string[];" ) ; // a union
309+ check ( "type T = (number & mixed)[];" ) ;
310+ check ( "type T = number & mixed[];" ) ; // an intersection
311+
312+ // Unary prefix `?`, with the above.
313+ // `?` binds tighter than `&` or `|`
314+ check ( "type T = ?(A & B);" ) ;
315+ check ( "type T = ?(A | B);" ) ;
316+ // `?` binds less tightly than `[]`
317+ check ( "type T = (?number)[];" ) ; // array of nullable
318+ check ( "type T = ?number[];" ) ; // nullable of array
319+
320+ // (Optional) indexed-access types, with the above.
321+ // `[…]` and `?.[…]` bind (their left) tighter than either `&` or `|`
322+ check ( "type T = (O & P)['x'];" ) ;
323+ check ( "type T = (O | P)['x'];" ) ;
324+ check ( "type T = (O & P)?.['x'];" ) ;
325+ check ( "type T = (O | P)?.['x'];" ) ;
326+ // `[…]` and `?.[…]` bind (their left) tighter than `?`
327+ check ( "type T = (?O)['x'];" ) ; // indexed-access of nullable
328+ check ( "type T = ?O['x'];" ) ; // nullable of indexed-access
329+ check ( "type T = (?O)?.['x'];" ) ; // optional-indexed-access of nullable
330+ check ( "type T = ?O?.['x'];" ) ; // nullable of optional-indexed-access
331+ // `[…]` and `?.[…]` provide brackets on their right, so skip parens:
332+ check ( "type T = A[B & C];" ) ;
333+ check ( "type T = A[B | C];" ) ;
334+ check ( "type T = A[?B];" ) ;
335+ check ( "type T = A[B[]];" ) ;
336+ check ( "type T = A[B[C]];" ) ;
337+ check ( "type T = A[B?.[C]];" ) ;
338+ check ( "type T = A?.[B & C];" ) ;
339+ check ( "type T = A?.[B | C];" ) ;
340+ check ( "type T = A?.[?B];" ) ;
341+ check ( "type T = A?.[B[]];" ) ;
342+ check ( "type T = A?.[B[C]];" ) ;
343+ check ( "type T = A?.[B?.[C]];" ) ;
344+ // `[…]` and `?.[…]` interact in a nonobvious way:
345+ // OptionalIndexedAccessType inside IndexedAccessType.
346+ check ( "type T = (O?.['x']['y'])['z'];" ) ; // indexed of optional-indexed
347+ check ( "type T = O?.['x']['y']['z'];" ) ; // optional-indexed throughout
348+
349+ // Function types.
350+ // Function binds less tightly than binary operators at right:
351+ check ( "type T = (() => number) & O;" ) ; // an intersection
352+ check ( "type T = (() => number) | void;" ) ; // a union
353+ check ( "type T = () => number | void;" ) ; // a function
354+ check ( "type T = (() => void)['x'];" ) ;
355+ check ( "type T = () => void['x'];" ) ; // a function
356+ check ( "type T = (() => void)?.['x'];" ) ;
357+ check ( "type T = () => void?.['x'];" ) ; // a function
358+ // … and less tightly than suffix operator:
359+ check ( "type T = (() => void)[];" ) ; // an array
360+ check ( "type T = () => void[];" ) ; // a function
361+
362+ // Function does bind tighter than prefix operator (how could it not?)
363+ checkEquiv ( "type T = ?() => void;" , "type T = ?(() => void);" ) ;
364+ // … and tighter than `&` or `|` at left (ditto):
365+ checkEquiv ( "type T = A | () => void;" , "type T = A | (() => void);" ) ;
366+ checkEquiv ( "type T = A & () => void;" , "type T = A & (() => void);" ) ;
367+ // … but we choose to insert parens anyway:
368+ check ( "type T = ?(() => void);" ) ;
369+ check ( "type T = A | (() => void);" ) ;
370+ check ( "type T = A & (() => void);" ) ;
371+ // We don't insert parens for the *right* operand of indexed access,
372+ // though, that'd be silly (sillier than writing such a type at all?):
373+ check ( "type T = A[() => void];" ) ;
374+ check ( "type T = A?.[() => void];" ) ;
375+
376+ // Here's one reason we insert those parens we don't strictly have to:
377+ // Even when the parent is something at left so that function binds
378+ // tighter than it, *its* parent (or further ancestor) might be
379+ // something at right that binds tighter than function.
380+ // E.g., union of nullable of function:
381+ check ( "type T = ?(() => void) | A;" ) ;
382+ checkEquiv ( "type T = ?() => void | A;" , "type T = ?() => (void | A);" ) ;
383+ // … or intersection of nullable of function:
384+ check ( "type T = ?(() => void) & A;" ) ;
385+ checkEquiv ( "type T = ?() => void & A;" , "type T = ?() => (void & A);" ) ;
386+ // … or array or (optional-)indexed-access of nullable of function:
387+ check ( "type T = ?(() => void)[];" ) ;
388+ check ( "type T = ?(() => void)['x'];" ) ;
389+ check ( "type T = ?(() => void)?.['x'];" ) ;
390+ // … or union of intersection:
391+ check ( "type T = A & (() => void) | B;" ) ;
392+ // Or for an example beyond the grandparent: union of cubic nullable:
393+ check ( "type T = ???(() => void) | B;" ) ;
394+ // … or union of intersection of nullable:
395+ check ( "type T = A & ?(() => void) | B;" ) ;
396+ } ) ;
298397} ) ;
0 commit comments