diff --git a/changelog_unreleased/typescript/pr-7631.md b/changelog_unreleased/typescript/pr-7631.md new file mode 100644 index 000000000000..a7b9b5047dde --- /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 fe82fba30585..91b62cce7b9d 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 87a825c03666..962b8e2b971a 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 e7fe7830a603..8ff7e937a879 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 000000000000..2c1c1d43c845 --- /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 b70c4f989347..e12dad1d3bfd 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 000000000000..48bd11f9e57b --- /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 000000000000..47d3492b9b50 --- /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 000000000000..34992613347d --- /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 000000000000..a613d087c00a --- /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';