diff --git a/changelog_unreleased/typescript/14279.md b/changelog_unreleased/typescript/14279.md new file mode 100644 index 000000000000..b15c9c37e649 --- /dev/null +++ b/changelog_unreleased/typescript/14279.md @@ -0,0 +1,16 @@ +#### Fix parens in inferred function return types with `extends` (#14279 by @fisker) + + +```ts +// Input +type Foo = T extends ((a) => a is infer R extends string) ? R : never; + +// Prettier stable (First format) +type Foo = T extends (a) => a is infer R extends string ? R : never; + +// Prettier stable (Second format) +SyntaxError: '?' expected. + +// Prettier main +type Foo = T extends ((a) => a is infer R extends string) ? R : never; +``` diff --git a/src/language-js/needs-parens.js b/src/language-js/needs-parens.js index 0fd0207dbf47..6155f0a7999d 100644 --- a/src/language-js/needs-parens.js +++ b/src/language-js/needs-parens.js @@ -466,22 +466,30 @@ function needsParens(path, options) { } case "TSConditionalType": - if (name === "extendsType" && parent.type === "TSConditionalType") { - return true; - } - // fallthrough case "TSFunctionType": case "TSConstructorType": if (name === "extendsType" && parent.type === "TSConditionalType") { - const returnTypeAnnotation = (node.returnType || node.typeAnnotation) - .typeAnnotation; + if (node.type === "TSConditionalType") { + return true; + } + + let { typeAnnotation } = node.returnType || node.typeAnnotation; + + if ( + typeAnnotation.type === "TSTypePredicate" && + typeAnnotation.typeAnnotation + ) { + typeAnnotation = typeAnnotation.typeAnnotation.typeAnnotation; + } + if ( - returnTypeAnnotation.type === "TSInferType" && - returnTypeAnnotation.typeParameter.constraint + typeAnnotation.type === "TSInferType" && + typeAnnotation.typeParameter.constraint ) { return true; } } + if (name === "checkType" && parent.type === "TSConditionalType") { return true; } diff --git a/tests/format/typescript/conditional-types/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/conditional-types/__snapshots__/jsfmt.spec.js.snap index e598947c2b6a..14c69d952f8f 100644 --- a/tests/format/typescript/conditional-types/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/typescript/conditional-types/__snapshots__/jsfmt.spec.js.snap @@ -278,20 +278,6 @@ type Unpacked = T extends (infer U)[] ================================================================================ `; -exports[`issue-13275.ts format 1`] = ` -====================================options===================================== -parsers: ["typescript"] -printWidth: 80 - | printWidth -=====================================input====================================== -type Foo = T extends ((...a: any[]) => infer R extends string) ? R : never; - -=====================================output===================================== -type Foo = T extends ((...a: any[]) => infer R extends string) ? R : never; - -================================================================================ -`; - exports[`nested-in-condition.ts format 1`] = ` ====================================options===================================== parsers: ["typescript"] @@ -431,3 +417,57 @@ type Unpacked = T extends (infer U)[] ================================================================================ `; + +exports[`parentheses.ts format 1`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +// #13275 +type Foo = T extends ((...a: any[]) => infer R extends string) ? R : never; +type Foo = T extends (new (...a: any[]) => infer R extends string) ? R : never; + +// #14275 +type Test = T extends (( + token: TSESTree.Token +) => token is infer U extends TSESTree.Token) + ? U + : TSESTree.Token; +type Test = T extends (( + token: TSESTree.Token +) => asserts token is infer U extends TSESTree.Token) + ? U + : TSESTree.Token; +type Test = T extends (new ( + token: TSESTree.Token +) => token is infer U extends TSESTree.Token) + ? U + : TSESTree.Token; + +=====================================output===================================== +// #13275 +type Foo = T extends ((...a: any[]) => infer R extends string) ? R : never; +type Foo = T extends (new (...a: any[]) => infer R extends string) + ? R + : never; + +// #14275 +type Test = T extends (( + token: TSESTree.Token +) => token is infer U extends TSESTree.Token) + ? U + : TSESTree.Token; +type Test = T extends (( + token: TSESTree.Token +) => asserts token is infer U extends TSESTree.Token) + ? U + : TSESTree.Token; +type Test = T extends (new ( + token: TSESTree.Token +) => token is infer U extends TSESTree.Token) + ? U + : TSESTree.Token; + +================================================================================ +`; diff --git a/tests/format/typescript/conditional-types/issue-13275.ts b/tests/format/typescript/conditional-types/issue-13275.ts deleted file mode 100644 index de1ccb2859aa..000000000000 --- a/tests/format/typescript/conditional-types/issue-13275.ts +++ /dev/null @@ -1 +0,0 @@ -type Foo = T extends ((...a: any[]) => infer R extends string) ? R : never; diff --git a/tests/format/typescript/conditional-types/parentheses.ts b/tests/format/typescript/conditional-types/parentheses.ts new file mode 100644 index 000000000000..ea27b6710564 --- /dev/null +++ b/tests/format/typescript/conditional-types/parentheses.ts @@ -0,0 +1,20 @@ +// #13275 +type Foo = T extends ((...a: any[]) => infer R extends string) ? R : never; +type Foo = T extends (new (...a: any[]) => infer R extends string) ? R : never; + +// #14275 +type Test = T extends (( + token: TSESTree.Token +) => token is infer U extends TSESTree.Token) + ? U + : TSESTree.Token; +type Test = T extends (( + token: TSESTree.Token +) => asserts token is infer U extends TSESTree.Token) + ? U + : TSESTree.Token; +type Test = T extends (new ( + token: TSESTree.Token +) => token is infer U extends TSESTree.Token) + ? U + : TSESTree.Token;