From 318bf2cb3392ac70e2875484aeefecd0f5bf7aef Mon Sep 17 00:00:00 2001 From: Georgii Dolzhykov Date: Wed, 11 Mar 2020 16:40:57 +0200 Subject: [PATCH] Partial support for TypeScript 3.8 (private fields, import/export type modifier) (#7631) * support TypeScript 3.8 * Update standard_private_fields.ts * improve tests for import/export type modifier * temporary hacks for private fields * comment out some tests, update snapshots * add changelog entry --- changelog_unreleased/typescript/pr-7631.md | 9 ++++ src/language-js/postprocess.js | 19 ++++++- src/language-js/printer-estree.js | 4 ++ .../__snapshots__/jsfmt.spec.js.snap | 54 +++++++++++++++++++ .../standard_private_fields.ts | 21 ++++++++ .../__snapshots__/jsfmt.spec.js.snap | 16 ++++++ tests/typescript_export/export-as-ns.ts | 2 + .../__snapshots__/jsfmt.spec.js.snap | 45 ++++++++++++++++ tests/typescript_import_export/jsfmt.spec.js | 2 + .../typescript_import_export/type-modifier.ts | 16 ++++++ 10 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 changelog_unreleased/typescript/pr-7631.md create mode 100644 tests/typescript_class/standard_private_fields.ts create mode 100644 tests/typescript_export/export-as-ns.ts create mode 100644 tests/typescript_import_export/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/typescript_import_export/jsfmt.spec.js create mode 100644 tests/typescript_import_export/type-modifier.ts diff --git a/changelog_unreleased/typescript/pr-7631.md b/changelog_unreleased/typescript/pr-7631.md new file mode 100644 index 000000000..a7b9b5047 --- /dev/null +++ b/changelog_unreleased/typescript/pr-7631.md @@ -0,0 +1,9 @@ +#### TypeScript 3.8 ([#7631](https://github.com/prettier/prettier/pull/7631) by [@thorn0](https://github.com/thorn0)) + +Prettier doesn't yet fully support the new syntax added in TypeScript 3.8: + +| Syntax | `typescript` parser | `babel-ts` parser | +| ------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ----------------- | +| [Type-Only Imports and Exports](https://devblogs.microsoft.com/typescript/announcing-typescript-3-8/#type-only-imports-exports) | ✔️ | ❌ | +| [ECMAScript Private Fields](https://devblogs.microsoft.com/typescript/announcing-typescript-3-8/#ecmascript-private-fields) | ✔️ | ✔️ | +| [`export * as ns`](https://devblogs.microsoft.com/typescript/announcing-typescript-3-8/#export-star-as-namespace-syntax) | ❌ | ❌ | diff --git a/src/language-js/postprocess.js b/src/language-js/postprocess.js index fe82fba30..91b62cce7 100644 --- a/src/language-js/postprocess.js +++ b/src/language-js/postprocess.js @@ -1,6 +1,9 @@ "use strict"; -const { getLast } = require("../common/util"); +const { + getLast, + getNextNonSpaceNonCommentCharacter +} = require("../common/util"); const { composeLoc, locEnd } = require("./loc"); function postprocess(ast, options) { @@ -48,6 +51,20 @@ function postprocess(ast, options) { node.end = getLast(node.expressions).end; } break; + case "ClassProperty": + // TODO: Temporary auto-generated node type. To remove when typescript-estree has proper support for private fields. + if ( + node.key && + node.key.type === "TSPrivateIdentifier" && + getNextNonSpaceNonCommentCharacter( + options.originalText, + node.key, + locEnd + ) === "?" + ) { + node.optional = true; + } + break; } }); diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index 9fefddb39..f5d2ff60a 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -3521,6 +3521,10 @@ function printPathNoParens(path, options, print, args) { case "PrivateName": return concat(["#", path.call(print, "id")]); + // TODO: Temporary auto-generated node type. To remove when typescript-estree has proper support for private fields. + case "TSPrivateIdentifier": + return n.escapedText; + case "TSConditionalType": return printTernaryOperator(path, options, print, { beforeParts: () => [ diff --git a/tests/typescript_class/__snapshots__/jsfmt.spec.js.snap b/tests/typescript_class/__snapshots__/jsfmt.spec.js.snap index e7fe7830a..8ff7e937a 100644 --- a/tests/typescript_class/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript_class/__snapshots__/jsfmt.spec.js.snap @@ -253,3 +253,57 @@ class User { ================================================================================ `; + +exports[`standard_private_fields.ts 1`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +class Square { + #sideLength: number; + readonly #area: number; + #unit?: string; + + constructor(sideLength: number, unit?: string) { + this.#sideLength = sideLength; + this.#area = this.#sideLength ** 2; + if (unit) { + this.#unit = unit; + } + } + + equals(other: any) { + return this.#sideLength === other.#sideLength; + } + + getArea() { + return this.#area + (this.#unit ?? 'px') + '²'; + } +} + +=====================================output===================================== +class Square { + #sideLength: number; + readonly #area: number; + #unit?: string; + + constructor(sideLength: number, unit?: string) { + this.#sideLength = sideLength; + this.#area = this.#sideLength ** 2; + if (unit) { + this.#unit = unit; + } + } + + equals(other: any) { + return this.#sideLength === other.#sideLength; + } + + getArea() { + return this.#area + (this.#unit ?? "px") + "²"; + } +} + +================================================================================ +`; diff --git a/tests/typescript_class/standard_private_fields.ts b/tests/typescript_class/standard_private_fields.ts new file mode 100644 index 000000000..2c1c1d43c --- /dev/null +++ b/tests/typescript_class/standard_private_fields.ts @@ -0,0 +1,21 @@ +class Square { + #sideLength: number; + readonly #area: number; + #unit?: string; + + constructor(sideLength: number, unit?: string) { + this.#sideLength = sideLength; + this.#area = this.#sideLength ** 2; + if (unit) { + this.#unit = unit; + } + } + + equals(other: any) { + return this.#sideLength === other.#sideLength; + } + + getArea() { + return this.#area + (this.#unit ?? 'px') + '²'; + } +} diff --git a/tests/typescript_export/__snapshots__/jsfmt.spec.js.snap b/tests/typescript_export/__snapshots__/jsfmt.spec.js.snap index b70c4f989..e12dad1d3 100644 --- a/tests/typescript_export/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript_export/__snapshots__/jsfmt.spec.js.snap @@ -60,6 +60,22 @@ declare module "hello" { ================================================================================ `; +exports[`export-as-ns.ts 1`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +// TODO: uncomment when typescript-estree gets support for this syntax +// export * as utilities from "./utilities.js"; + +=====================================output===================================== +// TODO: uncomment when typescript-estree gets support for this syntax +// export * as utilities from "./utilities.js"; + +================================================================================ +`; + exports[`export-class.js 1`] = ` ====================================options===================================== parsers: ["typescript"] diff --git a/tests/typescript_export/export-as-ns.ts b/tests/typescript_export/export-as-ns.ts new file mode 100644 index 000000000..48bd11f9e --- /dev/null +++ b/tests/typescript_export/export-as-ns.ts @@ -0,0 +1,2 @@ +// TODO: uncomment when typescript-estree gets support for this syntax +// export * as utilities from "./utilities.js"; diff --git a/tests/typescript_import_export/__snapshots__/jsfmt.spec.js.snap b/tests/typescript_import_export/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..47d3492b9 --- /dev/null +++ b/tests/typescript_import_export/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,45 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`type-modifier.ts 1`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +export type { SomeThing }; +export type { A as B }; +export type { B as C } from './a'; +export type { foo } from 'bar'; +export type * from 'bar'; +export type { foo }; + +// this should be treated as a normal import statement +import type from './foo'; + +import type { SomeThing } from "./some-module.js"; +import type { foo, bar } from 'baz'; +import type { foo as bar } from 'baz'; +import type * as foo from './bar'; +import type foo from 'bar'; +import type foo, { bar } from 'bar'; + +=====================================output===================================== +export type { SomeThing }; +export type { A as B }; +export type { B as C } from "./a"; +export type { foo } from "bar"; +export type * from "bar"; +export type { foo }; + +// this should be treated as a normal import statement +import type from "./foo"; + +import type { SomeThing } from "./some-module.js"; +import type { foo, bar } from "baz"; +import type { foo as bar } from "baz"; +import type * as foo from "./bar"; +import type foo from "bar"; +import type foo, { bar } from "bar"; + +================================================================================ +`; diff --git a/tests/typescript_import_export/jsfmt.spec.js b/tests/typescript_import_export/jsfmt.spec.js new file mode 100644 index 000000000..349926133 --- /dev/null +++ b/tests/typescript_import_export/jsfmt.spec.js @@ -0,0 +1,2 @@ +// TODO: remove disableBabelTS when Babel's TS plugin gets support for import/export `type` modifier +run_spec(__dirname, ["typescript"], { disableBabelTS: true }); diff --git a/tests/typescript_import_export/type-modifier.ts b/tests/typescript_import_export/type-modifier.ts new file mode 100644 index 000000000..a613d087c --- /dev/null +++ b/tests/typescript_import_export/type-modifier.ts @@ -0,0 +1,16 @@ +export type { SomeThing }; +export type { A as B }; +export type { B as C } from './a'; +export type { foo } from 'bar'; +export type * from 'bar'; +export type { foo }; + +// this should be treated as a normal import statement +import type from './foo'; + +import type { SomeThing } from "./some-module.js"; +import type { foo, bar } from 'baz'; +import type { foo as bar } from 'baz'; +import type * as foo from './bar'; +import type foo from 'bar'; +import type foo, { bar } from 'bar';