diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md
index 10b564ae..94da1ddc 100644
--- a/CHANGELOG.unreleased.md
+++ b/CHANGELOG.unreleased.md
@@ -44,6 +44,107 @@ const link = http://example.com;
-->
+#### TypeScript: Support for TypeScript 3.7 ([#6657] by [@cryrivers])
+
+Prettier 1.19 adds support for the features of the upcoming TypeScript 3.7 that introduce new syntax:
+
+- [Optional chaining](https://devblogs.microsoft.com/typescript/announcing-typescript-3-7-rc/#optional-chaining)
+- [Nullish coalescing](https://devblogs.microsoft.com/typescript/announcing-typescript-3-7-rc/#nullish-coalescing)
+- [Assertion functions](https://devblogs.microsoft.com/typescript/announcing-typescript-3-7-rc/#assertion-functions)
+- [`declare` modifier on class fields](https://github.com/microsoft/TypeScript/pull/33509)
+
+**NOTE:** A dependency upgrade for TypeScript 3.7 led to dropping Node 6 support for direct installation from GitHub. Prettier installed from NPM stays compatible with Node 4.
+
+##### Optional Chaining
+
+
+```ts
+// Input
+const longChain = obj?.a?.b?.c?.d?.e?.f?.g;
+const longChainCallExpression = obj.a?.(a,b,c).b?.(a,b,c).c?.(a,b,c).d?.(a,b,c).e?.(a,b,c).f?.(a,b,c)
+
+// Output (Prettier master)
+const longChain = obj?.a?.b?.c?.d?.e?.f?.g;
+const longChainCallExpression = obj
+ .a?.(a, b, c)
+ .b?.(a, b, c)
+ .c?.(a, b, c)
+ .d?.(a, b, c)
+ .e?.(a, b, c)
+ .f?.(a, b, c);
+```
+
+##### Nullish Coalescing
+
+
+```ts
+// Input
+const cond = null;
+const result = cond??'a';
+const longChain = cond??cond??cond??'b';
+
+// Output (Prettier master)
+const cond = null;
+const result = cond ?? "a";
+const longChain = cond ?? cond ?? cond ?? "b";
+```
+
+##### Assertion Functions
+
+
+```ts
+// Input
+function assertsString(x: any): asserts x {console.assert(typeof x === 'string');}
+function assertsStringWithGuard(x: any): asserts x is string {console.assert(typeof x === 'string');}
+
+// Output (Prettier master)
+function assertsString(x: any): asserts x {
+ console.assert(typeof x === "string");
+}
+function assertsStringWithGuard(x: any): asserts x is string {
+ console.assert(typeof x === "string");
+}
+```
+
+##### `declare` Modifier on Class Fields
+
+
+```ts
+// Input
+class B {p: number;}
+class C extends B {declare p: 256 | 1000;}
+
+// Output (Prettier master)
+class B {
+ p: number;
+}
+class C extends B {
+ declare p: 256 | 1000;
+}
+```
+
+#### TypeScript: Prettier removed `?` from optional computed class fields ([#6657] by [@cryrivers])
+
+Still happens if the field key is a complex expression, but has been fixed in this case:
+
+
+```ts
+// Input
+class Foo {
+ [bar]?: number;
+}
+
+// Output (Prettier stable)
+class Foo {
+ [bar]: number;
+}
+
+// Output (Prettier master)
+class Foo {
+ [bar]?: number;
+}
+```
+
#### API: Add `resolveConfig` option to `getFileInfo()` ([#6666] by [@kaicataldo])
Add a `resolveConfig: boolean` option to `prettier.getFileInfo()` that, when set to `true`, will resolve the configuration for the given file path. This allows consumers to take any overridden parsers into account.
@@ -52,6 +153,7 @@ Add a `resolveConfig: boolean` option to `prettier.getFileInfo()` that, when set
```js
+// Input
const addOne = add(1, ?); // apply from the left
addOne(2); // 3
@@ -1168,6 +1270,7 @@ new Map([
```
[#5682]: https://github.com/prettier/prettier/pull/5682
+[#6657]: https://github.com/prettier/prettier/pull/6657
[#5910]: https://github.com/prettier/prettier/pull/5910
[#6033]: https://github.com/prettier/prettier/pull/6033
[#6186]: https://github.com/prettier/prettier/pull/6186
@@ -1224,4 +1327,5 @@ new Map([
[@selvazhagan]: https://github.com/selvazhagan
[@chadian]: https://github.com/chadian
[@kaicataldo]: https://github.com/kaicataldo
+[@cryrivers]: https://github.com/Cryrivers
[@voithos]: https://github.com/voithos
diff --git a/package.json b/package.json
index 851474d8..3114cb47 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,7 @@
"license": "MIT",
"main": "./index.js",
"engines": {
- "node": ">=6"
+ "node": ">=8"
},
"dependencies": {
"@angular/compiler": "8.2.7",
@@ -19,7 +19,7 @@
"@babel/parser": "7.6.3",
"@glimmer/syntax": "0.41.0",
"@iarna/toml": "2.2.3",
- "@typescript-eslint/typescript-estree": "1.13.0",
+ "@typescript-eslint/typescript-estree": "2.5.1-alpha.5",
"angular-estree-parser": "1.1.5",
"angular-html-parser": "1.2.0",
"camelcase": "5.3.1",
@@ -67,7 +67,7 @@
"resolve": "1.12.0",
"semver": "6.3.0",
"string-width": "3.1.0",
- "typescript": "3.6.4",
+ "typescript": "3.7.1-rc",
"unicode-regex": "3.0.0",
"unified": "6.1.6",
"vnopts": "1.0.2",
diff --git a/src/common/util.js b/src/common/util.js
index 5db0e0ad..582f76ee 100644
--- a/src/common/util.js
+++ b/src/common/util.js
@@ -342,6 +342,7 @@ function startsWithNoLookaheadToken(node, forbidFunctionClassAndDoExpr) {
case "ObjectExpression":
return true;
case "MemberExpression":
+ case "OptionalMemberExpression":
return startsWithNoLookaheadToken(
node.object,
forbidFunctionClassAndDoExpr
@@ -353,6 +354,7 @@ function startsWithNoLookaheadToken(node, forbidFunctionClassAndDoExpr) {
}
return startsWithNoLookaheadToken(node.tag, forbidFunctionClassAndDoExpr);
case "CallExpression":
+ case "OptionalCallExpression":
if (node.callee.type === "FunctionExpression") {
// IIFEs are always already parenthesized
return false;
diff --git a/src/language-js/comments.js b/src/language-js/comments.js
index cf264945..6f339594 100644
--- a/src/language-js/comments.js
+++ b/src/language-js/comments.js
@@ -564,6 +564,7 @@ function handleCommentInEmptyParens(text, enclosingNode, comment, options) {
enclosingNode.type === "ObjectMethod") &&
enclosingNode.params.length === 0) ||
((enclosingNode.type === "CallExpression" ||
+ enclosingNode.type === "OptionalCallExpression" ||
enclosingNode.type === "NewExpression") &&
enclosingNode.arguments.length === 0))
) {
@@ -686,7 +687,8 @@ function handleBreakAndContinueStatementComments(enclosingNode, comment) {
function handleCallExpressionComments(precedingNode, enclosingNode, comment) {
if (
enclosingNode &&
- enclosingNode.type === "CallExpression" &&
+ (enclosingNode.type === "CallExpression" ||
+ enclosingNode.type === "OptionalCallExpression") &&
precedingNode &&
enclosingNode.callee === precedingNode &&
enclosingNode.arguments.length > 0
diff --git a/src/language-js/needs-parens.js b/src/language-js/needs-parens.js
index c8596ecf..d8201676 100644
--- a/src/language-js/needs-parens.js
+++ b/src/language-js/needs-parens.js
@@ -254,14 +254,16 @@ function needsParens(path, options) {
return true;
case "MemberExpression":
- return name === "object" && parent.object === node;
+ case "OptionalMemberExpression":
+ return name === "object";
case "TaggedTemplateExpression":
return true;
case "NewExpression":
case "CallExpression":
- return name === "callee" && parent.callee === node;
+ case "OptionalCallExpression":
+ return name === "callee";
case "BinaryExpression":
return parent.operator === "**" && name === "left";
@@ -306,7 +308,8 @@ function needsParens(path, options) {
case "CallExpression":
case "NewExpression":
- return name === "callee" && parent.callee === node;
+ case "OptionalCallExpression":
+ return name === "callee";
case "ClassExpression":
case "ClassDeclaration":
@@ -327,7 +330,7 @@ function needsParens(path, options) {
case "MemberExpression":
case "OptionalMemberExpression":
- return name === "object" && parent.object === node;
+ return name === "object";
case "AssignmentExpression":
return (
@@ -427,15 +430,16 @@ function needsParens(path, options) {
case "TSAsExpression":
case "TSNonNullExpression":
case "BindExpression":
- case "OptionalMemberExpression":
return true;
case "MemberExpression":
- return parent.object === node;
+ case "OptionalMemberExpression":
+ return name === "object";
case "NewExpression":
case "CallExpression":
- return parent.callee === node;
+ case "OptionalCallExpression":
+ return name === "callee";
case "ConditionalExpression":
return parent.test === node;
@@ -591,18 +595,19 @@ function needsParens(path, options) {
case "TypeCastExpression":
case "TSAsExpression":
case "TSNonNullExpression":
- case "OptionalMemberExpression":
return true;
case "NewExpression":
case "CallExpression":
- return name === "callee" && parent.callee === node;
+ case "OptionalCallExpression":
+ return name === "callee";
case "ConditionalExpression":
return name === "test" && parent.test === node;
case "MemberExpression":
- return name === "object" && parent.object === node;
+ case "OptionalMemberExpression":
+ return name === "object";
default:
return false;
@@ -612,7 +617,10 @@ function needsParens(path, options) {
switch (parent.type) {
case "NewExpression":
case "CallExpression":
- return name === "callee"; // Not strictly necessary, but it's clearer to the reader if IIFEs are wrapped in parentheses.
+ case "OptionalCallExpression":
+ // Not always necessary, but it's clearer to the reader if IIFEs are wrapped in parentheses.
+ // Is necessary if it is `expression` of `ExpressionStatement`.
+ return name === "callee";
case "TaggedTemplateExpression":
return true; // This is basically a kind of IIFE.
default:
@@ -621,13 +629,13 @@ function needsParens(path, options) {
case "ArrowFunctionExpression":
switch (parent.type) {
- case "CallExpression":
- return name === "callee";
-
case "NewExpression":
+ case "CallExpression":
+ case "OptionalCallExpression":
return name === "callee";
case "MemberExpression":
+ case "OptionalMemberExpression":
return name === "object";
case "TSAsExpression":
@@ -656,23 +664,32 @@ function needsParens(path, options) {
}
case "OptionalMemberExpression":
- return parent.type === "MemberExpression";
-
+ case "OptionalCallExpression":
+ if (
+ ((parent.type === "MemberExpression" && name === "object") ||
+ (parent.type === "CallExpression" && name === "callee")) &&
+ // workaround for https://github.com/facebook/flow/issues/8159
+ !(options.parser === "flow" && parent.range[0] === node.range[0])
+ ) {
+ return true;
+ }
+ // fallthrough
case "CallExpression":
case "MemberExpression":
case "TaggedTemplateExpression":
case "TSNonNullExpression":
if (
(parent.type === "BindExpression" || parent.type === "NewExpression") &&
- name === "callee" &&
- parent.callee === node
+ name === "callee"
) {
let object = node;
while (object) {
switch (object.type) {
case "CallExpression":
+ case "OptionalCallExpression":
return true;
case "MemberExpression":
+ case "OptionalMemberExpression":
case "BindExpression":
object = object.object;
break;
@@ -692,20 +709,14 @@ function needsParens(path, options) {
return false;
case "BindExpression":
- if (
- (parent.type === "BindExpression" &&
- name === "callee" &&
- parent.callee === node) ||
- (parent.type === "MemberExpression" &&
- name === "object" &&
- parent.object === node) ||
- (parent.type === "NewExpression" &&
- name === "callee" &&
- parent.callee === node)
- ) {
- return true;
- }
- return false;
+ return (
+ ((parent.type === "BindExpression" ||
+ parent.type === "NewExpression") &&
+ name === "callee") ||
+ ((parent.type === "MemberExpression" ||
+ parent.type === "OptionalMemberExpression") &&
+ name === "object")
+ );
case "NGPipeExpression":
if (
parent.type === "NGRoot" ||
@@ -725,25 +736,27 @@ function needsParens(path, options) {
case "JSXFragment":
case "JSXElement":
return (
- parent.type !== "ArrayExpression" &&
- parent.type !== "ArrowFunctionExpression" &&
- parent.type !== "AssignmentExpression" &&
- parent.type !== "AssignmentPattern" &&
- parent.type !== "BinaryExpression" &&
- parent.type !== "CallExpression" &&
- parent.type !== "ConditionalExpression" &&
- parent.type !== "ExpressionStatement" &&
- parent.type !== "JsExpressionRoot" &&
- parent.type !== "JSXAttribute" &&
- parent.type !== "JSXElement" &&
- parent.type !== "JSXExpressionContainer" &&
- parent.type !== "JSXFragment" &&
- parent.type !== "LogicalExpression" &&
- parent.type !== "ObjectProperty" &&
- parent.type !== "Property" &&
- parent.type !== "ReturnStatement" &&
- parent.type !== "TypeCastExpression" &&
- parent.type !== "VariableDeclarator"
+ name === "callee" ||
+ (parent.type !== "ArrayExpression" &&
+ parent.type !== "ArrowFunctionExpression" &&
+ parent.type !== "AssignmentExpression" &&
+ parent.type !== "AssignmentPattern" &&
+ parent.type !== "BinaryExpression" &&
+ parent.type !== "CallExpression" &&
+ parent.type !== "ConditionalExpression" &&
+ parent.type !== "ExpressionStatement" &&
+ parent.type !== "JsExpressionRoot" &&
+ parent.type !== "JSXAttribute" &&
+ parent.type !== "JSXElement" &&
+ parent.type !== "JSXExpressionContainer" &&
+ parent.type !== "JSXFragment" &&
+ parent.type !== "LogicalExpression" &&
+ parent.type !== "ObjectProperty" &&
+ parent.type !== "OptionalCallExpression" &&
+ parent.type !== "Property" &&
+ parent.type !== "ReturnStatement" &&
+ parent.type !== "TypeCastExpression" &&
+ parent.type !== "VariableDeclarator")
);
}
diff --git a/src/language-js/parser-typescript.js b/src/language-js/parser-typescript.js
index 8f99112f..35401525 100644
--- a/src/language-js/parser-typescript.js
+++ b/src/language-js/parser-typescript.js
@@ -43,10 +43,7 @@ function tryParseTypeScript(text, jsx) {
tokens: true,
comment: true,
useJSXTextNode: true,
- jsx,
- // Override logger function with noop,
- // to avoid unsupported version errors being logged
- loggerFn: () => {}
+ jsx
});
}
diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js
index 0696c55c..4bbb9193 100644
--- a/src/language-js/printer-estree.js
+++ b/src/language-js/printer-estree.js
@@ -570,7 +570,9 @@ function printPathNoParens(path, options, print, args) {
// c
// ).call()
if (
- (parent.type === "CallExpression" && parent.callee === n) ||
+ ((parent.type === "CallExpression" ||
+ parent.type === "OptionalCallExpression") &&
+ parent.callee === n) ||
parent.type === "UnaryExpression" ||
((parent.type === "MemberExpression" ||
parent.type === "OptionalMemberExpression") &&
@@ -596,7 +598,8 @@ function printPathNoParens(path, options, print, args) {
(n !== parent.body && parent.type === "ForStatement") ||
(parent.type === "ConditionalExpression" &&
(parentParent.type !== "ReturnStatement" &&
- parentParent.type !== "CallExpression"));
+ parentParent.type !== "CallExpression" &&
+ parentParent.type !== "OptionalCallExpression"));
const shouldIndentIfInlining =
parent.type === "AssignmentExpression" ||
@@ -2350,6 +2353,9 @@ function printPathNoParens(path, options, print, args) {
if (n.accessibility) {
parts.push(n.accessibility + " ");
}
+ if (n.declare) {
+ parts.push("declare ");
+ }
if (n.static) {
parts.push("static ");
}
@@ -3177,9 +3183,11 @@ function printPathNoParens(path, options, print, args) {
}
case "TSTypePredicate":
return concat([
+ n.asserts ? "asserts " : "",
path.call(print, "parameterName"),
- " is ",
- path.call(print, "typeAnnotation")
+ n.typeAnnotation
+ ? concat([" is ", path.call(print, "typeAnnotation")])
+ : ""
]);
case "TSNonNullExpression":
return concat([path.call(print, "expression"), "!"]);
@@ -5447,11 +5455,17 @@ function maybeWrapJSXElementInParens(path, elem, options) {
return elem;
}
- const shouldBreak = matchAncestorTypes(path, [
- "ArrowFunctionExpression",
- "CallExpression",
- "JSXExpressionContainer"
- ]);
+ const shouldBreak =
+ matchAncestorTypes(path, [
+ "ArrowFunctionExpression",
+ "CallExpression",
+ "JSXExpressionContainer"
+ ]) ||
+ matchAncestorTypes(path, [
+ "ArrowFunctionExpression",
+ "OptionalCallExpression",
+ "JSXExpressionContainer"
+ ]);
const needsParens = pathNeedsParens(path, options);
@@ -5816,7 +5830,8 @@ function willPrintOwnComments(path /*, options */) {
(isJSXNode(node) ||
hasFlowShorthandAnnotationComment(node) ||
(parent &&
- parent.type === "CallExpression" &&
+ (parent.type === "CallExpression" ||
+ parent.type === "OptionalCallExpression") &&
(hasFlowAnnotationComment(node.leadingComments) ||
hasFlowAnnotationComment(node.trailingComments))))) ||
(parent &&
diff --git a/src/language-js/utils.js b/src/language-js/utils.js
index cd60e378..8666b6eb 100644
--- a/src/language-js/utils.js
+++ b/src/language-js/utils.js
@@ -425,25 +425,29 @@ function isSimpleTemplateLiteral(node) {
// Allow `a.b.c`, `a.b[c]`, and `this.x.y`
if (
- (expr.type === "MemberExpression" ||
- expr.type === "OptionalMemberExpression") &&
- (expr.property.type === "Identifier" || expr.property.type === "Literal")
+ expr.type === "MemberExpression" ||
+ expr.type === "OptionalMemberExpression"
) {
- let ancestor = expr;
+ let head = expr;
while (
- ancestor.type === "MemberExpression" ||
- ancestor.type === "OptionalMemberExpression"
+ head.type === "MemberExpression" ||
+ head.type === "OptionalMemberExpression"
) {
- ancestor = ancestor.object;
- if (ancestor.comments) {
+ if (
+ head.property.type !== "Identifier" &&
+ head.property.type !== "Literal" &&
+ head.property.type !== "StringLiteral" &&
+ head.property.type !== "NumericLiteral"
+ ) {
+ return false;
+ }
+ head = head.object;
+ if (head.comments) {
return false;
}
}
- if (
- ancestor.type === "Identifier" ||
- ancestor.type === "ThisExpression"
- ) {
+ if (head.type === "Identifier" || head.type === "ThisExpression") {
return true;
}
diff --git a/tests/comments/__snapshots__/jsfmt.spec.js.snap b/tests/comments/__snapshots__/jsfmt.spec.js.snap
index 87824618..34cf5455 100644
--- a/tests/comments/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/comments/__snapshots__/jsfmt.spec.js.snap
@@ -325,6 +325,11 @@ React.render( // Warm any cache
container
);
+render?.( // Warm any cache
+ ,
+ container
+);
+
=====================================output=====================================
render(
// Warm any cache
@@ -338,6 +343,12 @@ React.render(
container
);
+render?.(
+ // Warm any cache
+ ,
+ container
+);
+
================================================================================
`;
@@ -357,6 +368,7 @@ function d() {
}
new Thing(/* dangling */);
Thing(/* dangling */);
+Thing?.(/* dangling */);
declare class Foo extends Qux {/* dangling */}
export /* dangling */{};
@@ -375,6 +387,7 @@ function d() {
}
new Thing(/* dangling */);
Thing(/* dangling */);
+Thing?.(/* dangling */);
declare class Foo extends Qux {
/* dangling */
}
diff --git a/tests/comments/call_comment.js b/tests/comments/call_comment.js
index ff2ea0ea..ac4af413 100644
--- a/tests/comments/call_comment.js
+++ b/tests/comments/call_comment.js
@@ -7,3 +7,8 @@ React.render( // Warm any cache
,
container
);
+
+render?.( // Warm any cache
+ ,
+ container
+);
diff --git a/tests/comments/dangling.js b/tests/comments/dangling.js
index 25939492..30ae9272 100644
--- a/tests/comments/dangling.js
+++ b/tests/comments/dangling.js
@@ -8,5 +8,6 @@ function d() {
}
new Thing(/* dangling */);
Thing(/* dangling */);
+Thing?.(/* dangling */);
declare class Foo extends Qux {/* dangling */}
export /* dangling */{};
diff --git a/tests/jsx/__snapshots__/jsfmt.spec.js.snap b/tests/jsx/__snapshots__/jsfmt.spec.js.snap
index c07256ba..31cb9d86 100644
--- a/tests/jsx/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/jsx/__snapshots__/jsfmt.spec.js.snap
@@ -4093,6 +4093,10 @@ a = [
+f?.();
+()();
+()?.();
+
=====================================output=====================================
a = [
;
+f?.();
+()();
+()?.();
+
================================================================================
`;
@@ -4131,6 +4139,10 @@ a = [
+f?.();
+()();
+()?.();
+
=====================================output=====================================
a = [
;
+f?.();
+()();
+()?.();
+
================================================================================
`;
@@ -4169,6 +4185,10 @@ a = [
+f?.();
+()();
+()?.();
+
=====================================output=====================================
a = [
;
+f?.();
+()();
+()?.();
+
================================================================================
`;
@@ -4207,6 +4231,10 @@ a = [
+f?.();
+()();
+()?.();
+
=====================================output=====================================
a = [
;
+f?.();
+()();
+()?.();
+
================================================================================
`;
diff --git a/tests/jsx/parens.js b/tests/jsx/parens.js
index 3a31f7c1..aacd7379 100644
--- a/tests/jsx/parens.js
+++ b/tests/jsx/parens.js
@@ -10,3 +10,7 @@ a = [
];
+
+f?.();
+()();
+()?.();
diff --git a/tests/nullish_coalescing/__snapshots__/jsfmt.spec.js.snap b/tests/nullish_coalescing/__snapshots__/jsfmt.spec.js.snap
index a85951d9..57a8c76a 100644
--- a/tests/nullish_coalescing/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/nullish_coalescing/__snapshots__/jsfmt.spec.js.snap
@@ -2,7 +2,7 @@
exports[`nullish_coalesing_operator.js 1`] = `
====================================options=====================================
-parsers: ["babel", "flow"]
+parsers: ["babel", "flow", "typescript"]
printWidth: 80
| printWidth
=====================================input======================================
diff --git a/tests/nullish_coalescing/jsfmt.spec.js b/tests/nullish_coalescing/jsfmt.spec.js
index e3b2b302..eb85eda6 100644
--- a/tests/nullish_coalescing/jsfmt.spec.js
+++ b/tests/nullish_coalescing/jsfmt.spec.js
@@ -1 +1 @@
-run_spec(__dirname, ["babel", "flow"]);
+run_spec(__dirname, ["babel", "flow", "typescript"]);
diff --git a/tests/optional_chaining/__snapshots__/jsfmt.spec.js.snap b/tests/optional_chaining/__snapshots__/jsfmt.spec.js.snap
index 72697a72..d317b7b4 100644
--- a/tests/optional_chaining/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/optional_chaining/__snapshots__/jsfmt.spec.js.snap
@@ -2,7 +2,7 @@
exports[`chaining.js 1`] = `
====================================options=====================================
-parsers: ["babel", "flow"]
+parsers: ["babel", "flow", "typescript"]
printWidth: 80
| printWidth
=====================================input======================================
@@ -35,8 +35,55 @@ a?.b[3].c?.(x).d.e?.f[3].g?.(y).h;
async function HelloWorld() {
var x = (await foo.bar.blah)?.hi;
+ a?.[await b];
+ (await x)?.();
}
+a[b?.c].d();
+a?.[b?.c].d();
+a[b?.c]?.d();
+a?.[b?.c]?.d();
+
+(one?.fn());
+(one?.two).fn();
+(one.two?.fn());
+(one.two?.three).fn();
+(one.two?.three?.fn());
+
+(one?.());
+(one?.())();
+(one?.())?.();
+
+(one?.()).two;
+
+a?.[b ? c : d];
+
+(-1)?.toFixed();
+(void fn)?.();
+(a && b)?.();
+(a ? b : c)?.();
+(function(){})?.();
+(() => f)?.();
+(()=>f)?.x;
+(a?.(x)).x;
+(aaaaaaaaaaaaaaaaaaaaaaaa&&aaaaaaaaaaaaaaaaaaaaaaaa&&aaaaaaaaaaaaaaaaaaaaaaaa)?.();
+
+let f = () => ({}?.());
+let g = () => ({}?.b);
+a = () => ({}?.() && a);
+a = () => ({}?.()() && a);
+a = () => ({}?.().b && a);
+a = () => ({}?.b && a);
+a = () => ({}?.b() && a);
+(a) => ({}?.()?.b && 0);
+(a) => ({}?.b?.b && 0);
+(x) => ({}?.()());
+(x) => ({}?.().b);
+(x) => ({}?.b());
+(x) => ({}?.b.b);
+({}?.a().b());
+({ a: 1 }?.entries());
+
=====================================output=====================================
var street = user.address?.street;
var fooValue = myForm.querySelector("input[name=foo]")?.value;
@@ -67,14 +114,65 @@ a?.b?.c.d?.e;
async function HelloWorld() {
var x = (await foo.bar.blah)?.hi;
+ a?.[await b];
+ (await x)?.();
}
+a[b?.c].d();
+a?.[b?.c].d();
+a[b?.c]?.d();
+a?.[b?.c]?.d();
+
+one?.fn();
+(one?.two).fn();
+one.two?.fn();
+(one.two?.three).fn();
+one.two?.three?.fn();
+
+one?.();
+(one?.())();
+one?.()?.();
+
+(one?.()).two;
+
+a?.[b ? c : d];
+
+(-1)?.toFixed();
+(void fn)?.();
+(a && b)?.();
+(a ? b : c)?.();
+(function() {})?.();
+(() => f)?.();
+(() => f)?.x;
+(a?.(x)).x;
+(
+ aaaaaaaaaaaaaaaaaaaaaaaa &&
+ aaaaaaaaaaaaaaaaaaaaaaaa &&
+ aaaaaaaaaaaaaaaaaaaaaaaa
+)?.();
+
+let f = () => ({}?.());
+let g = () => ({}?.b);
+a = () => ({}?.() && a);
+a = () => ({}?.()() && a);
+a = () => ({}?.().b && a);
+a = () => ({}?.b && a);
+a = () => ({}?.b() && a);
+a => ({}?.()?.b && 0);
+a => ({}?.b?.b && 0);
+x => ({}?.()());
+x => ({}?.().b);
+x => ({}?.b());
+x => ({}?.b.b);
+({}?.a().b());
+({ a: 1 }?.entries());
+
================================================================================
`;
exports[`comments.js 1`] = `
====================================options=====================================
-parsers: ["babel", "flow"]
+parsers: ["babel", "flow", "typescript"]
printWidth: 80
| printWidth
=====================================input======================================
diff --git a/tests/optional_chaining/chaining.js b/tests/optional_chaining/chaining.js
index eb30e566..a169179b 100644
--- a/tests/optional_chaining/chaining.js
+++ b/tests/optional_chaining/chaining.js
@@ -27,4 +27,51 @@ a?.b[3].c?.(x).d.e?.f[3].g?.(y).h;
async function HelloWorld() {
var x = (await foo.bar.blah)?.hi;
+ a?.[await b];
+ (await x)?.();
}
+
+a[b?.c].d();
+a?.[b?.c].d();
+a[b?.c]?.d();
+a?.[b?.c]?.d();
+
+(one?.fn());
+(one?.two).fn();
+(one.two?.fn());
+(one.two?.three).fn();
+(one.two?.three?.fn());
+
+(one?.());
+(one?.())();
+(one?.())?.();
+
+(one?.()).two;
+
+a?.[b ? c : d];
+
+(-1)?.toFixed();
+(void fn)?.();
+(a && b)?.();
+(a ? b : c)?.();
+(function(){})?.();
+(() => f)?.();
+(()=>f)?.x;
+(a?.(x)).x;
+(aaaaaaaaaaaaaaaaaaaaaaaa&&aaaaaaaaaaaaaaaaaaaaaaaa&&aaaaaaaaaaaaaaaaaaaaaaaa)?.();
+
+let f = () => ({}?.());
+let g = () => ({}?.b);
+a = () => ({}?.() && a);
+a = () => ({}?.()() && a);
+a = () => ({}?.().b && a);
+a = () => ({}?.b && a);
+a = () => ({}?.b() && a);
+(a) => ({}?.()?.b && 0);
+(a) => ({}?.b?.b && 0);
+(x) => ({}?.()());
+(x) => ({}?.().b);
+(x) => ({}?.b());
+(x) => ({}?.b.b);
+({}?.a().b());
+({ a: 1 }?.entries());
diff --git a/tests/optional_chaining/jsfmt.spec.js b/tests/optional_chaining/jsfmt.spec.js
index e3b2b302..eb85eda6 100644
--- a/tests/optional_chaining/jsfmt.spec.js
+++ b/tests/optional_chaining/jsfmt.spec.js
@@ -1 +1 @@
-run_spec(__dirname, ["babel", "flow"]);
+run_spec(__dirname, ["babel", "flow", "typescript"]);
diff --git a/tests/template_literals/__snapshots__/jsfmt.spec.js.snap b/tests/template_literals/__snapshots__/jsfmt.spec.js.snap
index 3457abfa..4768cc0d 100644
--- a/tests/template_literals/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/template_literals/__snapshots__/jsfmt.spec.js.snap
@@ -2,7 +2,7 @@
exports[`css-prop.js 1`] = `
====================================options=====================================
-parsers: ["flow"]
+parsers: ["babel", "flow", "typescript"]
printWidth: 80
| printWidth
=====================================input======================================
@@ -73,15 +73,15 @@ const TestComponent = ({ children, ...props }) => (
exports[`expressions.js 1`] = `
====================================options=====================================
-parsers: ["flow"]
+parsers: ["babel", "flow", "typescript"]
printWidth: 80
| printWidth
=====================================input======================================
-const long = \`long \${a//comment
+const long1 = \`long \${a//comment
.b} long longlong \${a.b.c.d.e} long longlong \${a.b.c.d.e} long longlong \${a.b.c.d.e} long long\`;
-const long = \`long \${a.b.c.d.e} long longlong \${loooooooooooooooooong} long longlong \${loooooooooooooooooong} long longlong \${loooooooooooooooooong} long long\`;
+const long2 = \`long \${a.b.c.d.e} long longlong \${loooooooooooooooooong} long longlong \${loooooooooooooooooong} long longlong \${loooooooooooooooooong} long long\`;
-const long = \`long long long long long long long long long long long \${a.b.c.d.e} long long long long long long long long long long long long long\`;
+const long3 = \`long long long long long long long long long long long \${a.b.c.d.e} long long long long long long long long long long long long long\`;
const description =
\`The value of the \${cssName} css of the \${this._name} element\`;
@@ -125,14 +125,14 @@ descirbe('something', () => {
throw new Error(\`pretty-format: Option "theme" has a key "\${key}" whose value "\${value}" is undefined in ansi-styles.\`,)
=====================================output=====================================
-const long = \`long \${
+const long1 = \`long \${
a.b //comment
} long longlong \${a.b.c.d.e} long longlong \${a.b.c.d.e} long longlong \${
a.b.c.d.e
} long long\`;
-const long = \`long \${a.b.c.d.e} long longlong \${loooooooooooooooooong} long longlong \${loooooooooooooooooong} long longlong \${loooooooooooooooooong} long long\`;
+const long2 = \`long \${a.b.c.d.e} long longlong \${loooooooooooooooooong} long longlong \${loooooooooooooooooong} long longlong \${loooooooooooooooooong} long long\`;
-const long = \`long long long long long long long long long long long \${a.b.c.d.e} long long long long long long long long long long long long long\`;
+const long3 = \`long long long long long long long long long long long \${a.b.c.d.e} long long long long long long long long long long long long long\`;
const description = \`The value of the \${cssName} css of the \${this._name} element\`;
@@ -193,7 +193,7 @@ throw new Error(
exports[`styled-components-with-expressions.js 1`] = `
====================================options=====================================
-parsers: ["flow"]
+parsers: ["babel", "flow", "typescript"]
printWidth: 80
| printWidth
=====================================input======================================
@@ -294,7 +294,7 @@ const header = css\`
exports[`styled-jsx.js 1`] = `
====================================options=====================================
-parsers: ["flow"]
+parsers: ["babel", "flow", "typescript"]
printWidth: 80
| printWidth
=====================================input======================================
@@ -484,7 +484,7 @@ const headerGlobal = css.global\`
exports[`styled-jsx-with-expressions.js 1`] = `
====================================options=====================================
-parsers: ["flow"]
+parsers: ["babel", "flow", "typescript"]
printWidth: 80
| printWidth
=====================================input======================================
diff --git a/tests/template_literals/expressions.js b/tests/template_literals/expressions.js
index c6e3febd..c68f1176 100644
--- a/tests/template_literals/expressions.js
+++ b/tests/template_literals/expressions.js
@@ -1,8 +1,8 @@
-const long = `long ${a//comment
+const long1 = `long ${a//comment
.b} long longlong ${a.b.c.d.e} long longlong ${a.b.c.d.e} long longlong ${a.b.c.d.e} long long`;
-const long = `long ${a.b.c.d.e} long longlong ${loooooooooooooooooong} long longlong ${loooooooooooooooooong} long longlong ${loooooooooooooooooong} long long`;
+const long2 = `long ${a.b.c.d.e} long longlong ${loooooooooooooooooong} long longlong ${loooooooooooooooooong} long longlong ${loooooooooooooooooong} long long`;
-const long = `long long long long long long long long long long long ${a.b.c.d.e} long long long long long long long long long long long long long`;
+const long3 = `long long long long long long long long long long long ${a.b.c.d.e} long long long long long long long long long long long long long`;
const description =
`The value of the ${cssName} css of the ${this._name} element`;
diff --git a/tests/template_literals/jsfmt.spec.js b/tests/template_literals/jsfmt.spec.js
index b9a90898..eb85eda6 100644
--- a/tests/template_literals/jsfmt.spec.js
+++ b/tests/template_literals/jsfmt.spec.js
@@ -1 +1 @@
-run_spec(__dirname, ["flow"]);
+run_spec(__dirname, ["babel", "flow", "typescript"]);
diff --git a/tests/typescript_assert/__snapshots__/jsfmt.spec.js.snap b/tests/typescript_assert/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 00000000..aa33a20c
--- /dev/null
+++ b/tests/typescript_assert/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,68 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`index.ts 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+const assertString = (x: any): asserts x => {
+ console.assert(typeof x === 'string');
+}
+
+function assertsString(x: any): asserts x {
+ console.assert(typeof x === 'string');
+}
+
+const assertStringWithGuard = (x: any): asserts x is string => {
+ console.assert(typeof x === 'string');
+}
+
+function assertsStringWithGuard(x: any): asserts x is string {
+ console.assert(typeof x === 'string');
+}
+
+interface AssertFoo {
+ isString(node: any): asserts node;
+}
+
+class AssertsFoo {
+ isBar(): asserts this {
+ return;
+ }
+ isBaz = (): asserts this => {
+ return;
+ }
+}
+=====================================output=====================================
+const assertString = (x: any): asserts x => {
+ console.assert(typeof x === "string");
+};
+
+function assertsString(x: any): asserts x {
+ console.assert(typeof x === "string");
+}
+
+const assertStringWithGuard = (x: any): asserts x is string => {
+ console.assert(typeof x === "string");
+};
+
+function assertsStringWithGuard(x: any): asserts x is string {
+ console.assert(typeof x === "string");
+}
+
+interface AssertFoo {
+ isString(node: any): asserts node;
+}
+
+class AssertsFoo {
+ isBar(): asserts this {
+ return;
+ }
+ isBaz = (): asserts this => {
+ return;
+ };
+}
+
+================================================================================
+`;
diff --git a/tests/typescript_assert/index.ts b/tests/typescript_assert/index.ts
new file mode 100644
index 00000000..e09ee8ea
--- /dev/null
+++ b/tests/typescript_assert/index.ts
@@ -0,0 +1,28 @@
+const assertString = (x: any): asserts x => {
+ console.assert(typeof x === 'string');
+}
+
+function assertsString(x: any): asserts x {
+ console.assert(typeof x === 'string');
+}
+
+const assertStringWithGuard = (x: any): asserts x is string => {
+ console.assert(typeof x === 'string');
+}
+
+function assertsStringWithGuard(x: any): asserts x is string {
+ console.assert(typeof x === 'string');
+}
+
+interface AssertFoo {
+ isString(node: any): asserts node;
+}
+
+class AssertsFoo {
+ isBar(): asserts this {
+ return;
+ }
+ isBaz = (): asserts this => {
+ return;
+ }
+}
\ No newline at end of file
diff --git a/tests/typescript_assert/jsfmt.spec.js b/tests/typescript_assert/jsfmt.spec.js
new file mode 100644
index 00000000..2ea3bb6e
--- /dev/null
+++ b/tests/typescript_assert/jsfmt.spec.js
@@ -0,0 +1 @@
+run_spec(__dirname, ["typescript"]);
diff --git a/tests/typescript_declare/__snapshots__/jsfmt.spec.js.snap b/tests/typescript_declare/__snapshots__/jsfmt.spec.js.snap
index 44fb4d59..2e79f1b0 100644
--- a/tests/typescript_declare/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/typescript_declare/__snapshots__/jsfmt.spec.js.snap
@@ -1,5 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`declare_class_fields.ts 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+class B {p: number;}
+class C extends B {declare p: 256 | 1000;}
+
+=====================================output=====================================
+class B {
+ p: number;
+}
+class C extends B {
+ declare p: 256 | 1000;
+}
+
+================================================================================
+`;
+
exports[`declare_enum.ts 1`] = `
====================================options=====================================
parsers: ["typescript"]
diff --git a/tests/typescript_declare/declare_class_fields.ts b/tests/typescript_declare/declare_class_fields.ts
new file mode 100644
index 00000000..447d7a37
--- /dev/null
+++ b/tests/typescript_declare/declare_class_fields.ts
@@ -0,0 +1,2 @@
+class B {p: number;}
+class C extends B {declare p: 256 | 1000;}
diff --git a/yarn.lock b/yarn.lock
index 8c91d834..e22dc9ab 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -975,13 +975,16 @@
dependencies:
"@types/yargs-parser" "*"
-"@typescript-eslint/typescript-estree@1.13.0":
- version "1.13.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz#8140f17d0f60c03619798f1d628b8434913dc32e"
- integrity sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==
+"@typescript-eslint/typescript-estree@2.5.1-alpha.5":
+ version "2.5.1-alpha.5"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.5.1-alpha.5.tgz#7dc6a29bc55a89640378948894039933c9c370ea"
+ integrity sha512-R0bXoqvgLw2epTtCRjZZUo+vwGFqRqcQ54kd8Dcm+lVY7Mxp5Wf9CDunojkB2EGwwG0olbihJIRQI1GZaewBtg==
dependencies:
+ debug "^4.1.1"
+ glob "^7.1.4"
+ is-glob "^4.0.1"
lodash.unescape "4.0.1"
- semver "5.5.0"
+ semver "^6.3.0"
"@webassemblyjs/ast@1.8.5":
version "1.8.5"
@@ -2351,7 +2354,7 @@ debug@^3.1.0:
dependencies:
ms "2.0.0"
-debug@^4.0.1:
+debug@^4.0.1, debug@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
@@ -5254,6 +5257,7 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
normalize-path@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+ integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
normalize-path@^2.0.1, normalize-path@^2.1.1:
version "2.1.1"
@@ -6477,11 +6481,7 @@ schema-utils@^2.4.1:
version "5.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
-semver@5.5.0:
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
-
-semver@6.3.0, semver@^6.0.0, semver@^6.1.2:
+semver@6.3.0, semver@^6.0.0, semver@^6.1.2, semver@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
@@ -7161,10 +7161,10 @@ typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
-typescript@3.6.4:
- version "3.6.4"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.4.tgz#b18752bb3792bc1a0281335f7f6ebf1bbfc5b91d"
- integrity sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==
+typescript@3.7.1-rc:
+ version "3.7.1-rc"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.1-rc.tgz#2054b872d67f8dc732e36c1df397f9327f37ada0"
+ integrity sha512-2rMtWppLsaPvmpXsoIAXWDBQVnJMw1ITGGSnidMuayLj9iCmMRT69ncKZw/Mk5rXfJkilApKucWQZxproALoRw==
uglify-js@^3.1.4:
version "3.6.0"