diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md
index 7c55b54aca6..3a504fe7d83 100644
--- a/packages/eslint-plugin/README.md
+++ b/packages/eslint-plugin/README.md
@@ -218,6 +218,7 @@ In these cases, we create what we call an extension rule; a rule within our plug
| [`@typescript-eslint/no-unused-vars`](./docs/rules/no-unused-vars.md) | Disallow unused variables | :heavy_check_mark: | | |
| [`@typescript-eslint/no-use-before-define`](./docs/rules/no-use-before-define.md) | Disallow the use of variables before they are defined | | | |
| [`@typescript-eslint/no-useless-constructor`](./docs/rules/no-useless-constructor.md) | Disallow unnecessary constructors | | | |
+| [`@typescript-eslint/object-curly-spacing`](./docs/rules/object-curly-spacing.md) | Enforce consistent spacing inside braces | | :wrench: | |
| [`@typescript-eslint/quotes`](./docs/rules/quotes.md) | Enforce the consistent use of either backticks, double, or single quotes | | :wrench: | |
| [`@typescript-eslint/require-await`](./docs/rules/require-await.md) | Disallow async functions which have no `await` expression | :heavy_check_mark: | | :thought_balloon: |
| [`@typescript-eslint/return-await`](./docs/rules/return-await.md) | Enforces consistent returning of awaited values | | :wrench: | :thought_balloon: |
diff --git a/packages/eslint-plugin/docs/rules/object-curly-spacing.md b/packages/eslint-plugin/docs/rules/object-curly-spacing.md
new file mode 100644
index 00000000000..44bd35afb59
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/object-curly-spacing.md
@@ -0,0 +1,22 @@
+# Enforce consistent spacing inside braces (`object-curly-spacing`)
+
+## Rule Details
+
+This rule extends the base [`eslint/object-curly-spacing`](https://eslint.org/docs/rules/object-curly-spacing) rule.
+It adds support for TypeScript's object types.
+
+## How to use
+
+```cjson
+{
+ // note you must disable the base rule as it can report incorrect errors
+ "object-curly-spacing": "off",
+ "@typescript-eslint/object-curly-spacing": ["error"]
+}
+```
+
+## Options
+
+See [`eslint/object-curly-spacing` options](https://eslint.org/docs/rules/object-curly-spacing#options).
+
+Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/object-curly-spacing.md)
diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts
index 36cd690afa0..98afc4ae3a4 100644
--- a/packages/eslint-plugin/src/configs/all.ts
+++ b/packages/eslint-plugin/src/configs/all.ts
@@ -113,6 +113,8 @@ export = {
'@typescript-eslint/no-useless-constructor': 'error',
'@typescript-eslint/no-var-requires': 'error',
'@typescript-eslint/non-nullable-type-assertion-style': 'error',
+ 'object-curly-spacing': 'off',
+ '@typescript-eslint/object-curly-spacing': 'error',
'@typescript-eslint/prefer-as-const': 'error',
'@typescript-eslint/prefer-enum-initializers': 'error',
'@typescript-eslint/prefer-for-of': 'error',
diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts
index 1be014e46c5..ffa70e57a29 100644
--- a/packages/eslint-plugin/src/rules/index.ts
+++ b/packages/eslint-plugin/src/rules/index.ts
@@ -79,6 +79,7 @@ import noUseBeforeDefine from './no-use-before-define';
import noUselessConstructor from './no-useless-constructor';
import noVarRequires from './no-var-requires';
import nonNullableTypeAssertionStyle from './non-nullable-type-assertion-style';
+import objectCurlySpacing from './object-curly-spacing';
import preferAsConst from './prefer-as-const';
import preferEnumInitializers from './prefer-enum-initializers';
import preferForOf from './prefer-for-of';
@@ -195,6 +196,7 @@ export default {
'no-useless-constructor': noUselessConstructor,
'no-var-requires': noVarRequires,
'non-nullable-type-assertion-style': nonNullableTypeAssertionStyle,
+ 'object-curly-spacing': objectCurlySpacing,
'prefer-as-const': preferAsConst,
'prefer-enum-initializers': preferEnumInitializers,
'prefer-for-of': preferForOf,
diff --git a/packages/eslint-plugin/src/rules/object-curly-spacing.ts b/packages/eslint-plugin/src/rules/object-curly-spacing.ts
new file mode 100644
index 00000000000..581e7ac1555
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/object-curly-spacing.ts
@@ -0,0 +1,267 @@
+import {
+ AST_NODE_TYPES,
+ AST_TOKEN_TYPES,
+ TSESTree,
+} from '@typescript-eslint/experimental-utils';
+import baseRule from 'eslint/lib/rules/object-curly-spacing';
+import {
+ createRule,
+ InferMessageIdsTypeFromRule,
+ InferOptionsTypeFromRule,
+ isClosingBraceToken,
+ isClosingBracketToken,
+ isTokenOnSameLine,
+} from '../util';
+
+export type Options = InferOptionsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
+
+export default createRule({
+ name: 'object-curly-spacing',
+ meta: {
+ ...baseRule.meta,
+ docs: {
+ description: 'Enforce consistent spacing inside braces',
+ category: 'Stylistic Issues',
+ recommended: false,
+ extendsBaseRule: true,
+ },
+ },
+ defaultOptions: ['never'],
+ create(context) {
+ const spaced = context.options[0] === 'always';
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Determines whether an option is set, relative to the spacing option.
+ * If spaced is "always", then check whether option is set to false.
+ * If spaced is "never", then check whether option is set to true.
+ * @param option The option to exclude.
+ * @returns Whether or not the property is excluded.
+ */
+ function isOptionSet(
+ option: 'arraysInObjects' | 'objectsInObjects',
+ ): boolean {
+ return context.options[1]
+ ? context.options[1][option] === !spaced
+ : false;
+ }
+
+ const options = {
+ spaced,
+ arraysInObjectsException: isOptionSet('arraysInObjects'),
+ objectsInObjectsException: isOptionSet('objectsInObjects'),
+ };
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Reports that there shouldn't be a space after the first token
+ * @param node The node to report in the event of an error.
+ * @param token The token to use for the report.
+ */
+ function reportNoBeginningSpace(
+ node: TSESTree.TSTypeLiteral,
+ token: TSESTree.Token,
+ ): void {
+ const nextToken = context
+ .getSourceCode()
+ .getTokenAfter(token, { includeComments: true })!;
+
+ context.report({
+ node,
+ loc: { start: token.loc.end, end: nextToken.loc.start },
+ messageId: 'unexpectedSpaceAfter',
+ data: {
+ token: token.value,
+ },
+ fix(fixer) {
+ return fixer.removeRange([token.range[1], nextToken.range[0]]);
+ },
+ });
+ }
+
+ /**
+ * Reports that there shouldn't be a space before the last token
+ * @param node The node to report in the event of an error.
+ * @param token The token to use for the report.
+ */
+ function reportNoEndingSpace(
+ node: TSESTree.TSTypeLiteral,
+ token: TSESTree.Token,
+ ): void {
+ const previousToken = context
+ .getSourceCode()
+ .getTokenBefore(token, { includeComments: true })!;
+
+ context.report({
+ node,
+ loc: { start: previousToken.loc.end, end: token.loc.start },
+ messageId: 'unexpectedSpaceBefore',
+ data: {
+ token: token.value,
+ },
+ fix(fixer) {
+ return fixer.removeRange([previousToken.range[1], token.range[0]]);
+ },
+ });
+ }
+
+ /**
+ * Reports that there should be a space after the first token
+ * @param node The node to report in the event of an error.
+ * @param token The token to use for the report.
+ */
+ function reportRequiredBeginningSpace(
+ node: TSESTree.TSTypeLiteral,
+ token: TSESTree.Token,
+ ): void {
+ context.report({
+ node,
+ loc: token.loc,
+ messageId: 'requireSpaceAfter',
+ data: {
+ token: token.value,
+ },
+ fix(fixer) {
+ return fixer.insertTextAfter(token, ' ');
+ },
+ });
+ }
+
+ /**
+ * Reports that there should be a space before the last token
+ * @param node The node to report in the event of an error.
+ * @param token The token to use for the report.
+ */
+ function reportRequiredEndingSpace(
+ node: TSESTree.TSTypeLiteral,
+ token: TSESTree.Token,
+ ): void {
+ context.report({
+ node,
+ loc: token.loc,
+ messageId: 'requireSpaceBefore',
+ data: {
+ token: token.value,
+ },
+ fix(fixer) {
+ return fixer.insertTextBefore(token, ' ');
+ },
+ });
+ }
+
+ /**
+ * Determines if spacing in curly braces is valid.
+ * @param node The AST node to check.
+ * @param first The first token to check (should be the opening brace)
+ * @param second The second token to check (should be first after the opening brace)
+ * @param penultimate The penultimate token to check (should be last before closing brace)
+ * @param last The last token to check (should be closing brace)
+ */
+ function validateBraceSpacing(
+ node: TSESTree.TSTypeLiteral,
+ first: TSESTree.Token,
+ second: TSESTree.Token | TSESTree.Comment,
+ penultimate: TSESTree.Token | TSESTree.Comment,
+ last: TSESTree.Token,
+ ): void {
+ if (isTokenOnSameLine(first, second)) {
+ const firstSpaced = sourceCode.isSpaceBetween!(first, second);
+ const secondType = sourceCode.getNodeByRangeIndex(second.range[0])!
+ .type;
+
+ const openingCurlyBraceMustBeSpaced =
+ options.arraysInObjectsException &&
+ secondType === AST_NODE_TYPES.TSIndexSignature
+ ? !options.spaced
+ : options.spaced;
+
+ if (openingCurlyBraceMustBeSpaced && !firstSpaced) {
+ reportRequiredBeginningSpace(node, first);
+ }
+ if (
+ !openingCurlyBraceMustBeSpaced &&
+ firstSpaced &&
+ second.type !== AST_TOKEN_TYPES.Line
+ ) {
+ reportNoBeginningSpace(node, first);
+ }
+ }
+
+ if (isTokenOnSameLine(penultimate, last)) {
+ const shouldCheckPenultimate =
+ (options.arraysInObjectsException &&
+ isClosingBracketToken(penultimate)) ||
+ (options.objectsInObjectsException &&
+ isClosingBraceToken(penultimate));
+ const penultimateType =
+ shouldCheckPenultimate &&
+ sourceCode.getNodeByRangeIndex(penultimate.range[0])!.type;
+
+ const closingCurlyBraceMustBeSpaced =
+ (options.arraysInObjectsException &&
+ penultimateType === AST_NODE_TYPES.TSTupleType) ||
+ (options.objectsInObjectsException &&
+ penultimateType === AST_NODE_TYPES.TSTypeLiteral)
+ ? !options.spaced
+ : options.spaced;
+
+ const lastSpaced = sourceCode.isSpaceBetween!(penultimate, last);
+
+ if (closingCurlyBraceMustBeSpaced && !lastSpaced) {
+ reportRequiredEndingSpace(node, last);
+ }
+ if (!closingCurlyBraceMustBeSpaced && lastSpaced) {
+ reportNoEndingSpace(node, last);
+ }
+ }
+ }
+
+ /**
+ * Gets '}' token of an object node.
+ *
+ * Because the last token of object patterns might be a type annotation,
+ * this traverses tokens preceded by the last property, then returns the
+ * first '}' token.
+ * @param node The node to get. This node is an
+ * ObjectExpression or an ObjectPattern. And this node has one or
+ * more properties.
+ * @returns '}' token.
+ */
+ function getClosingBraceOfObject(
+ node: TSESTree.TSTypeLiteral,
+ ): TSESTree.Token | null {
+ const lastProperty = node.members[node.members.length - 1];
+
+ return sourceCode.getTokenAfter(lastProperty, isClosingBraceToken);
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ const rules = baseRule.create(context);
+ return {
+ ...rules,
+ TSTypeLiteral(node: TSESTree.TSTypeLiteral): void {
+ if (node.members.length === 0) {
+ return;
+ }
+
+ const first = sourceCode.getFirstToken(node)!;
+ const last = getClosingBraceOfObject(node)!;
+ const second = sourceCode.getTokenAfter(first, {
+ includeComments: true,
+ })!;
+ const penultimate = sourceCode.getTokenBefore(last, {
+ includeComments: true,
+ })!;
+
+ validateBraceSpacing(node, first, second, penultimate, last);
+ },
+ };
+ },
+});
diff --git a/packages/eslint-plugin/tests/rules/object-curly-spacing.test.ts b/packages/eslint-plugin/tests/rules/object-curly-spacing.test.ts
new file mode 100644
index 00000000000..5c28cbef7ed
--- /dev/null
+++ b/packages/eslint-plugin/tests/rules/object-curly-spacing.test.ts
@@ -0,0 +1,1955 @@
+/* eslint-disable eslint-comments/no-use */
+// this rule tests the position of braces, which prettier will want to fix and break the tests
+/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */
+/* eslint-enable eslint-comments/no-use */
+
+import { AST_NODE_TYPES } from '@typescript-eslint/experimental-utils';
+import rule from '../../src/rules/object-curly-spacing';
+import { RuleTester } from '../RuleTester';
+
+const ruleTester = new RuleTester({
+ parser: '@typescript-eslint/parser',
+});
+
+ruleTester.run('object-curly-spacing', rule, {
+ valid: [
+ // always - object literals
+ { code: 'var obj = { foo: bar, baz: qux };', options: ['always'] },
+ {
+ code: 'var obj = { foo: { bar: quxx }, baz: qux };',
+ options: ['always'],
+ },
+ { code: 'var obj = {\nfoo: bar,\nbaz: qux\n};', options: ['always'] },
+ { code: 'var obj = { /**/foo:bar/**/ };', options: ['always'] },
+ { code: 'var obj = { //\nfoo:bar };', options: ['always'] },
+
+ // always - destructuring
+ {
+ code: 'var { x } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { x, y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { x,y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {\nx,y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {\nx,y\n} = z',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { /**/x/**/ } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { //\nx } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { x = 10, y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { x: { z }, y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {\ny,\n} = x',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { y, } = x',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { y: x } = x',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+
+ // always - import / export
+ {
+ code: "import door from 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import * as door from 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import { door } from 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {\ndoor } from 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import { /**/door/**/ } from 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import { //\ndoor } from 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export { door } from 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import { house, mouse } from 'caravan'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import house, { mouse } from 'caravan'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import door, { house, mouse } from 'caravan'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'var door = 0;export { door }',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import { bar as x } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import { x, } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {\nx,\n} from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export { x, } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {\nx,\n} from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export { /**/x/**/ } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export { //\nx } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'var x = 1;\nexport { /**/x/**/ };',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'var x = 1;\nexport { //\nx };',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+
+ // always - empty object
+ { code: 'var foo = {};', options: ['always'] },
+
+ // always - objectsInObjects
+ {
+ code: "var obj = { 'foo': { 'bar': 1, 'baz': 2 }};",
+ options: ['always', { objectsInObjects: false }],
+ },
+ {
+ code: 'var a = { noop: function () {} };',
+ options: ['always', { objectsInObjects: false }],
+ },
+ {
+ code: 'var { y: { z }} = x',
+ options: ['always', { objectsInObjects: false }],
+ parserOptions: { ecmaVersion: 6 },
+ },
+
+ // always - arraysInObjects
+ {
+ code: "var obj = { 'foo': [ 1, 2 ]};",
+ options: ['always', { arraysInObjects: false }],
+ },
+ {
+ code: 'var a = { thingInList: list[0] };',
+ options: ['always', { arraysInObjects: false }],
+ },
+
+ // always - arraysInObjects, objectsInObjects
+ {
+ code: "var obj = { 'qux': [ 1, 2 ], 'foo': { 'bar': 1, 'baz': 2 }};",
+ options: ['always', { arraysInObjects: false, objectsInObjects: false }],
+ },
+
+ // always - arraysInObjects, objectsInObjects (reverse)
+ {
+ code: "var obj = { 'foo': { 'bar': 1, 'baz': 2 }, 'qux': [ 1, 2 ]};",
+ options: ['always', { arraysInObjects: false, objectsInObjects: false }],
+ },
+
+ // never
+ { code: 'var obj = {foo: bar,\nbaz: qux\n};', options: ['never'] },
+ { code: 'var obj = {\nfoo: bar,\nbaz: qux};', options: ['never'] },
+
+ // never - object literals
+ { code: 'var obj = {foo: bar, baz: qux};', options: ['never'] },
+ { code: 'var obj = {foo: {bar: quxx}, baz: qux};', options: ['never'] },
+ { code: 'var obj = {foo: {\nbar: quxx}, baz: qux\n};', options: ['never'] },
+ { code: 'var obj = {foo: {\nbar: quxx\n}, baz: qux};', options: ['never'] },
+ { code: 'var obj = {\nfoo: bar,\nbaz: qux\n};', options: ['never'] },
+ { code: 'var obj = {foo: bar, baz: qux /* */};', options: ['never'] },
+ { code: 'var obj = {/* */ foo: bar, baz: qux};', options: ['never'] },
+ { code: 'var obj = {//\n foo: bar};', options: ['never'] },
+ {
+ code: 'var obj = { // line comment exception\n foo: bar};',
+ options: ['never'],
+ },
+
+ // never - destructuring
+ {
+ code: 'var {x} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {x, y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {x,y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {\nx,y\n} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {x = 10} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {x = 10, y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {x: {z}, y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {\nx: {z\n}, y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {\ny,\n} = x',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {y,} = x',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {y:x} = x',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {/* */ y} = x',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {y /* */} = x',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {//\n y} = x',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { // line comment exception\n y} = x',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+
+ // never - import / export
+ {
+ code: "import door from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import * as door from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {/* */ door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {/* */ door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {door /* */} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {door /* */} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {//\n door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {//\n door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'var door = foo;\nexport {//\n door}',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import { // line comment exception\n door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export { // line comment exception\n door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'var door = foo; export { // line comment exception\n door}',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {\ndoor} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {\ndoor\n} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {house,mouse} from 'caravan'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {house, mouse} from 'caravan'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'var door = 0;export {door}',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import x, {bar} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import x, {bar, baz} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {bar as y} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {x,} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {\nx,\n} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {x,} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {\nx,\n} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+
+ // never - empty object
+ { code: 'var foo = {};', options: ['never'] },
+
+ // never - objectsInObjects
+ {
+ code: "var obj = {'foo': {'bar': 1, 'baz': 2} };",
+ options: ['never', { objectsInObjects: true }],
+ },
+
+ /*
+ * https://github.com/eslint/eslint/issues/3658
+ * Empty cases.
+ */
+ { code: 'var {} = foo;', parserOptions: { ecmaVersion: 6 } },
+ { code: 'var [] = foo;', parserOptions: { ecmaVersion: 6 } },
+ { code: 'var {a: {}} = foo;', parserOptions: { ecmaVersion: 6 } },
+ { code: 'var {a: []} = foo;', parserOptions: { ecmaVersion: 6 } },
+ {
+ code: "import {} from 'foo';",
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {} from 'foo';",
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'export {};',
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'var {} = foo;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var [] = foo;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {a: {}} = foo;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {a: []} = foo;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: "import {} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'export {};',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+
+ // https://github.com/eslint/eslint/issues/6940
+ {
+ code: 'function foo ({a, b}: Props) {\n}',
+ options: ['never'],
+ },
+
+ // default - object literal types
+ {
+ code: 'const x:{}',
+ },
+ {
+ code: 'const x:{ }',
+ },
+ {
+ code: 'const x:{f: number}',
+ },
+ {
+ code: 'const x:{ // line-comment\nf: number\n}',
+ },
+ {
+ code: 'const x:{// line-comment\nf: number\n}',
+ },
+ {
+ code: 'const x:{/* inline-comment */f: number/* inline-comment */}',
+ },
+ {
+ code: 'const x:{\nf: number\n}',
+ },
+ {
+ code: 'const x:{f: {g: number}}',
+ },
+ {
+ code: 'const x:{f: [number]}',
+ },
+ {
+ code: 'const x:{[key: string]: value}',
+ },
+ {
+ code: 'const x:{[key: string]: [number]}',
+ },
+
+ // never - object literal types
+ {
+ code: 'const x:{f: {g: number} }',
+ options: ['never', { objectsInObjects: true }],
+ },
+ {
+ code: 'const x:{f: {g: number}}',
+ options: ['never', { objectsInObjects: false }],
+ },
+ {
+ code: 'const x:{f: () => {g: number} }',
+ options: ['never', { objectsInObjects: true }],
+ },
+ {
+ code: 'const x:{f: () => {g: number}}',
+ options: ['never', { objectsInObjects: false }],
+ },
+ {
+ code: 'const x:{f: [number] }',
+ options: ['never', { arraysInObjects: true }],
+ },
+ {
+ code: 'const x:{f: [ number ]}',
+ options: ['never', { arraysInObjects: false }],
+ },
+ {
+ code: 'const x:{ [key: string]: value}',
+ options: ['never', { arraysInObjects: true }],
+ },
+ {
+ code: 'const x:{[key: string]: value}',
+ options: ['never', { arraysInObjects: false }],
+ },
+ {
+ code: 'const x:{ [key: string]: [number] }',
+ options: ['never', { arraysInObjects: true }],
+ },
+ {
+ code: 'const x:{[key: string]: [number]}',
+ options: ['never', { arraysInObjects: false }],
+ },
+
+ // always - object literal types
+ {
+ code: 'const x:{}',
+ options: ['always'],
+ },
+ {
+ code: 'const x:{ }',
+ options: ['always'],
+ },
+ {
+ code: 'const x:{ f: number }',
+ options: ['always'],
+ },
+ {
+ code: 'const x:{ // line-comment\nf: number\n}',
+ options: ['always'],
+ },
+ {
+ code: 'const x:{ /* inline-comment */ f: number /* inline-comment */ }',
+ options: ['always'],
+ },
+ {
+ code: 'const x:{\nf: number\n}',
+ options: ['always'],
+ },
+ {
+ code: 'const x:{ f: [number] }',
+ options: ['always'],
+ },
+
+ // always - objectsInObjects
+ {
+ code: 'const x:{ f: { g: number } }',
+ options: ['always', { objectsInObjects: true }],
+ },
+ {
+ code: 'const x:{ f: { g: number }}',
+ options: ['always', { objectsInObjects: false }],
+ },
+ {
+ code: 'const x:{ f: () => { g: number } }',
+ options: ['always', { objectsInObjects: true }],
+ },
+ {
+ code: 'const x:{ f: () => { g: number }}',
+ options: ['always', { objectsInObjects: false }],
+ },
+
+ // always - arraysInObjects
+ {
+ code: 'const x:{ f: [number] }',
+ options: ['always', { arraysInObjects: true }],
+ },
+ {
+ code: 'const x:{ f: [ number ]}',
+ options: ['always', { arraysInObjects: false }],
+ },
+ {
+ code: 'const x:{ [key: string]: value }',
+ options: ['always', { arraysInObjects: true }],
+ },
+ {
+ code: 'const x:{[key: string]: value }',
+ options: ['always', { arraysInObjects: false }],
+ },
+ {
+ code: 'const x:{ [key: string]: [number] }',
+ options: ['always', { arraysInObjects: true }],
+ },
+ {
+ code: 'const x:{[key: string]: [number]}',
+ options: ['always', { arraysInObjects: false }],
+ },
+ ],
+
+ invalid: [
+ {
+ code: "import {bar} from 'foo.js';",
+ output: "import { bar } from 'foo.js';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 8,
+ endLine: 1,
+ endColumn: 9,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 13,
+ },
+ ],
+ },
+ {
+ code: "import { bar as y} from 'foo.js';",
+ output: "import { bar as y } from 'foo.js';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 18,
+ endLine: 1,
+ endColumn: 19,
+ },
+ ],
+ },
+ {
+ code: "import {bar as y} from 'foo.js';",
+ output: "import { bar as y } from 'foo.js';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 8,
+ endLine: 1,
+ endColumn: 9,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 17,
+ endLine: 1,
+ endColumn: 18,
+ },
+ ],
+ },
+ {
+ code: "import { bar} from 'foo.js';",
+ output: "import { bar } from 'foo.js';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 13,
+ endLine: 1,
+ endColumn: 14,
+ },
+ ],
+ },
+ {
+ code: "import x, { bar} from 'foo';",
+ output: "import x, { bar } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 16,
+ endLine: 1,
+ endColumn: 17,
+ },
+ ],
+ },
+ {
+ code: "import x, { bar/* */} from 'foo';",
+ output: "import x, { bar/* */ } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 21,
+ endLine: 1,
+ endColumn: 22,
+ },
+ ],
+ },
+ {
+ code: "import x, {/* */bar } from 'foo';",
+ output: "import x, { /* */bar } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: "import x, {//\n bar } from 'foo';",
+ output: "import x, { //\n bar } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: "import x, { bar, baz} from 'foo';",
+ output: "import x, { bar, baz } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 21,
+ endLine: 1,
+ endColumn: 22,
+ },
+ ],
+ },
+ {
+ code: "import x, {bar} from 'foo';",
+ output: "import x, { bar } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 15,
+ endLine: 1,
+ endColumn: 16,
+ },
+ ],
+ },
+ {
+ code: "import x, {bar, baz} from 'foo';",
+ output: "import x, { bar, baz } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 20,
+ endLine: 1,
+ endColumn: 21,
+ },
+ ],
+ },
+ {
+ code: "import {bar,} from 'foo';",
+ output: "import { bar, } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 8,
+ endLine: 1,
+ endColumn: 9,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 13,
+ endLine: 1,
+ endColumn: 14,
+ },
+ ],
+ },
+ {
+ code: "import { bar, } from 'foo';",
+ output: "import {bar,} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 9,
+ endLine: 1,
+ endColumn: 10,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 14,
+ endLine: 1,
+ endColumn: 15,
+ },
+ ],
+ },
+ {
+ code: "import { /* */ bar, /* */ } from 'foo';",
+ output: "import {/* */ bar, /* */} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 9,
+ endLine: 1,
+ endColumn: 10,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 26,
+ endLine: 1,
+ endColumn: 27,
+ },
+ ],
+ },
+ {
+ code: 'var bar = 0;\nexport {bar};',
+ output: 'var bar = 0;\nexport { bar };',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ExportNamedDeclaration,
+ line: 2,
+ column: 8,
+ endLine: 2,
+ endColumn: 9,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ExportNamedDeclaration,
+ line: 2,
+ column: 12,
+ },
+ ],
+ },
+ {
+ code: 'var bar = 0;\nexport {/* */ bar /* */};',
+ output: 'var bar = 0;\nexport { /* */ bar /* */ };',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ExportNamedDeclaration,
+ line: 2,
+ column: 8,
+ endLine: 2,
+ endColumn: 9,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ExportNamedDeclaration,
+ line: 2,
+ column: 24,
+ endLine: 2,
+ endColumn: 25,
+ },
+ ],
+ },
+ {
+ code: 'var bar = 0;\nexport {//\n bar };',
+ output: 'var bar = 0;\nexport { //\n bar };',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ExportNamedDeclaration,
+ line: 2,
+ column: 8,
+ endLine: 2,
+ endColumn: 9,
+ },
+ ],
+ },
+ {
+ code: 'var bar = 0;\nexport { /* */ bar /* */ };',
+ output: 'var bar = 0;\nexport {/* */ bar /* */};',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ExportNamedDeclaration,
+ line: 2,
+ column: 9,
+ endLine: 2,
+ endColumn: 10,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ExportNamedDeclaration,
+ line: 2,
+ column: 25,
+ endLine: 2,
+ endColumn: 26,
+ },
+ ],
+ },
+
+ // always - arraysInObjects
+ {
+ code: "var obj = { 'foo': [ 1, 2 ] };",
+ output: "var obj = { 'foo': [ 1, 2 ]};",
+ options: ['always', { arraysInObjects: false }],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 28,
+ endLine: 1,
+ endColumn: 29,
+ },
+ ],
+ },
+ {
+ code: "var obj = { 'foo': [ 1, 2 ] , 'bar': [ 'baz', 'qux' ] };",
+ output: "var obj = { 'foo': [ 1, 2 ] , 'bar': [ 'baz', 'qux' ]};",
+ options: ['always', { arraysInObjects: false }],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 54,
+ endLine: 1,
+ endColumn: 55,
+ },
+ ],
+ },
+
+ // always-objectsInObjects
+ {
+ code: "var obj = { 'foo': { 'bar': 1, 'baz': 2 } };",
+ output: "var obj = { 'foo': { 'bar': 1, 'baz': 2 }};",
+ options: ['always', { objectsInObjects: false }],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 42,
+ endLine: 1,
+ endColumn: 43,
+ },
+ ],
+ },
+ {
+ code: "var obj = { 'foo': [ 1, 2 ] , 'bar': { 'baz': 1, 'qux': 2 } };",
+ output: "var obj = { 'foo': [ 1, 2 ] , 'bar': { 'baz': 1, 'qux': 2 }};",
+ options: ['always', { objectsInObjects: false }],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 60,
+ endLine: 1,
+ endColumn: 61,
+ },
+ ],
+ },
+
+ // always-destructuring trailing comma
+ {
+ code: 'var { a,} = x;',
+ output: 'var { a, } = x;',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 9,
+ endLine: 1,
+ endColumn: 10,
+ },
+ ],
+ },
+ {
+ code: 'var {a, } = x;',
+ output: 'var {a,} = x;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 8,
+ endLine: 1,
+ endColumn: 9,
+ },
+ ],
+ },
+ {
+ code: 'var {a:b } = x;',
+ output: 'var {a:b} = x;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 9,
+ endLine: 1,
+ endColumn: 10,
+ },
+ ],
+ },
+ {
+ code: 'var { a:b } = x;',
+ output: 'var {a:b} = x;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 6,
+ endLine: 1,
+ endColumn: 7,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 10,
+ endLine: 1,
+ endColumn: 11,
+ },
+ ],
+ },
+ {
+ code: 'var { a:b } = x;',
+ output: 'var {a:b} = x;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 6,
+ endLine: 1,
+ endColumn: 8,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 13,
+ },
+ ],
+ },
+ {
+ code: 'var { a:b } = x;',
+ output: 'var {a:b} = x;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 6,
+ endLine: 1,
+ endColumn: 9,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 16,
+ },
+ ],
+ },
+
+ // never-objectsInObjects
+ {
+ code: "var obj = {'foo': {'bar': 1, 'baz': 2}};",
+ output: "var obj = {'foo': {'bar': 1, 'baz': 2} };",
+ options: ['never', { objectsInObjects: true }],
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 39,
+ endLine: 1,
+ endColumn: 40,
+ },
+ ],
+ },
+ {
+ code: "var obj = {'foo': [1, 2] , 'bar': {'baz': 1, 'qux': 2}};",
+ output: "var obj = {'foo': [1, 2] , 'bar': {'baz': 1, 'qux': 2} };",
+ options: ['never', { objectsInObjects: true }],
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 55,
+ endLine: 1,
+ endColumn: 56,
+ },
+ ],
+ },
+
+ // always & never
+ {
+ code: 'var obj = {foo: bar, baz: qux};',
+ output: 'var obj = { foo: bar, baz: qux };',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 30,
+ endLine: 1,
+ endColumn: 31,
+ },
+ ],
+ },
+ {
+ code: 'var obj = {foo: bar, baz: qux };',
+ output: 'var obj = { foo: bar, baz: qux };',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: 'var obj = {/* */foo: bar, baz: qux };',
+ output: 'var obj = { /* */foo: bar, baz: qux };',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: 'var obj = {//\n foo: bar };',
+ output: 'var obj = { //\n foo: bar };',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { foo: bar, baz: qux};',
+ output: 'var obj = { foo: bar, baz: qux };',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 31,
+ endLine: 1,
+ endColumn: 32,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { foo: bar, baz: qux/* */};',
+ output: 'var obj = { foo: bar, baz: qux/* */ };',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 36,
+ endLine: 1,
+ endColumn: 37,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { foo: bar, baz: qux };',
+ output: 'var obj = {foo: bar, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 13,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 31,
+ endLine: 1,
+ endColumn: 32,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { foo: bar, baz: qux };',
+ output: 'var obj = {foo: bar, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 14,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 32,
+ endLine: 1,
+ endColumn: 33,
+ },
+ ],
+ },
+ {
+ code: 'var obj = {foo: bar, baz: qux };',
+ output: 'var obj = {foo: bar, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 30,
+ endLine: 1,
+ endColumn: 31,
+ },
+ ],
+ },
+ {
+ code: 'var obj = {foo: bar, baz: qux };',
+ output: 'var obj = {foo: bar, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 30,
+ endLine: 1,
+ endColumn: 32,
+ },
+ ],
+ },
+ {
+ code: 'var obj = {foo: bar, baz: qux /* */ };',
+ output: 'var obj = {foo: bar, baz: qux /* */};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 36,
+ endLine: 1,
+ endColumn: 37,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { foo: bar, baz: qux};',
+ output: 'var obj = {foo: bar, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 13,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { foo: bar, baz: qux};',
+ output: 'var obj = {foo: bar, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 14,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { /* */ foo: bar, baz: qux};',
+ output: 'var obj = {/* */ foo: bar, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 13,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { // line comment exception\n foo: bar };',
+ output: 'var obj = { // line comment exception\n foo: bar};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 2,
+ column: 10,
+ endLine: 2,
+ endColumn: 11,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { foo: { bar: quxx}, baz: qux};',
+ output: 'var obj = {foo: {bar: quxx}, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 13,
+ },
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 19,
+ endLine: 1,
+ endColumn: 20,
+ },
+ ],
+ },
+ {
+ code: 'var obj = {foo: {bar: quxx }, baz: qux };',
+ output: 'var obj = {foo: {bar: quxx}, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 27,
+ endLine: 1,
+ endColumn: 28,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 39,
+ endLine: 1,
+ endColumn: 40,
+ },
+ ],
+ },
+ {
+ code: 'export const thing = {value: 1 };',
+ output: 'export const thing = { value: 1 };',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 22,
+ endLine: 1,
+ endColumn: 23,
+ },
+ ],
+ },
+
+ // destructuring
+ {
+ code: 'var {x, y} = y',
+ output: 'var { x, y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 5,
+ endLine: 1,
+ endColumn: 6,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 10,
+ endLine: 1,
+ endColumn: 11,
+ },
+ ],
+ },
+ {
+ code: 'var { x, y} = y',
+ output: 'var { x, y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: 'var { x, y/* */} = y',
+ output: 'var { x, y/* */ } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 16,
+ endLine: 1,
+ endColumn: 17,
+ },
+ ],
+ },
+ {
+ code: 'var {/* */x, y } = y',
+ output: 'var { /* */x, y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 5,
+ endLine: 1,
+ endColumn: 6,
+ },
+ ],
+ },
+ {
+ code: 'var {//\n x } = y',
+ output: 'var { //\n x } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 5,
+ endLine: 1,
+ endColumn: 6,
+ },
+ ],
+ },
+ {
+ code: 'var { x, y } = y',
+ output: 'var {x, y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 6,
+ endLine: 1,
+ endColumn: 7,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: 'var {x, y } = y',
+ output: 'var {x, y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 10,
+ endLine: 1,
+ endColumn: 11,
+ },
+ ],
+ },
+ {
+ code: 'var {x, y/* */ } = y',
+ output: 'var {x, y/* */} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 15,
+ endLine: 1,
+ endColumn: 16,
+ },
+ ],
+ },
+ {
+ code: 'var { /* */x, y} = y',
+ output: 'var {/* */x, y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 6,
+ endLine: 1,
+ endColumn: 7,
+ },
+ ],
+ },
+ {
+ code: 'var { x=10} = y',
+ output: 'var { x=10 } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: 'var {x=10 } = y',
+ output: 'var { x=10 } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 5,
+ endLine: 1,
+ endColumn: 6,
+ },
+ ],
+ },
+
+ // never - arraysInObjects
+ {
+ code: "var obj = {'foo': [1, 2]};",
+ output: "var obj = {'foo': [1, 2] };",
+ options: ['never', { arraysInObjects: true }],
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 25,
+ endLine: 1,
+ endColumn: 26,
+ },
+ ],
+ },
+ {
+ code: "var obj = {'foo': [1, 2] , 'bar': ['baz', 'qux']};",
+ output: "var obj = {'foo': [1, 2] , 'bar': ['baz', 'qux'] };",
+ options: ['never', { arraysInObjects: true }],
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 49,
+ endLine: 1,
+ endColumn: 50,
+ },
+ ],
+ },
+
+ // https://github.com/eslint/eslint/issues/6940
+ {
+ code: 'function foo ({a, b }: Props) {\n}',
+ output: 'function foo ({a, b}: Props) {\n}',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 20,
+ endLine: 1,
+ endColumn: 21,
+ },
+ ],
+ },
+
+ // object literal types
+ {
+ code: 'type x = { f: number }',
+ output: 'type x = {f: number}',
+ errors: [
+ { messageId: 'unexpectedSpaceAfter' },
+ { messageId: 'unexpectedSpaceBefore' },
+ ],
+ },
+ {
+ code: 'type x = { f: number}',
+ output: 'type x = {f: number}',
+ errors: [{ messageId: 'unexpectedSpaceAfter' }],
+ },
+ {
+ code: 'type x = {f: number }',
+ output: 'type x = {f: number}',
+ errors: [{ messageId: 'unexpectedSpaceBefore' }],
+ },
+ {
+ code: 'type x = {f: number}',
+ output: 'type x = { f: number }',
+ options: ['always'],
+ errors: [
+ { messageId: 'requireSpaceAfter' },
+ { messageId: 'requireSpaceBefore' },
+ ],
+ },
+ {
+ code: 'type x = {f: number }',
+ output: 'type x = { f: number }',
+ options: ['always'],
+ errors: [{ messageId: 'requireSpaceAfter' }],
+ },
+ {
+ code: 'type x = { f: number}',
+ output: 'type x = { f: number }',
+ options: ['always'],
+ errors: [{ messageId: 'requireSpaceBefore' }],
+ },
+ ],
+});
diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts
index ca726d6fb83..49e48541f87 100644
--- a/packages/eslint-plugin/typings/eslint-rules.d.ts
+++ b/packages/eslint-plugin/typings/eslint-rules.d.ts
@@ -830,3 +830,28 @@ declare module 'eslint/lib/rules/prefer-const' {
>;
export = rule;
}
+
+declare module 'eslint/lib/rules/object-curly-spacing' {
+ import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils';
+
+ const rule: TSESLint.RuleModule<
+ | 'requireSpaceBefore'
+ | 'requireSpaceAfter'
+ | 'unexpectedSpaceBefore'
+ | 'unexpectedSpaceAfter',
+ [
+ 'always' | 'never',
+ {
+ arraysInObjects?: boolean;
+ objectsInObjects?: boolean;
+ }?,
+ ],
+ {
+ ObjectPattern(node: TSESTree.ObjectPattern): void;
+ ObjectExpression(node: TSESTree.ObjectExpression): void;
+ ImportDeclaration(node: TSESTree.ImportDeclaration): void;
+ ExportNamedDeclaration(node: TSESTree.ExportNamedDeclaration): void;
+ }
+ >;
+ export = rule;
+}