;
+ const o = { ...a };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 25,
+ line: 3,
+ messageId: 'noMapSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ const ref = new WeakRef({ a: 1 });
+ const o = { ...ref };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 27,
+ line: 3,
+ messageId: 'noClassInstanceSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ const promise = new Promise(() => {});
+ const o = { ...promise };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 31,
+ line: 3,
+ messageId: 'noPromiseSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ function withPromise>(promise: P) {
+ return { ...promise };
+ }
+ `,
+ errors: [
+ {
+ column: 20,
+ endColumn: 30,
+ line: 3,
+ messageId: 'noPromiseSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ declare const maybePromise: Promise | { a: number };
+ const o = { ...maybePromise };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 36,
+ line: 3,
+ messageId: 'noPromiseSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ declare const promise: Promise & { a: number };
+ const o = { ...promise };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 31,
+ line: 3,
+ messageId: 'noPromiseSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ declare function getPromise(): Promise;
+ const o = { ...getPromise() };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 36,
+ line: 3,
+ messageId: 'noPromiseSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ declare function getPromise>(arg: T): T;
+ const o = { ...getPromise() };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 36,
+ line: 3,
+ messageId: 'noPromiseSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ function f() {}
+
+ const o = { ...f };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 25,
+ line: 4,
+ messageId: 'noFunctionSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ interface FunctionWithProps {
+ (): string;
+ prop: boolean;
+ }
+
+ type FunctionWithoutProps = () => string;
+
+ declare const obj: FunctionWithProps | FunctionWithoutProps | object;
+
+ const o = { ...obj };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 27,
+ line: 11,
+ messageId: 'noFunctionSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ const f = () => {};
+
+ const o = { ...f };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 25,
+ line: 4,
+ messageId: 'noFunctionSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ declare function f(): void;
+
+ const o = { ...f };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 25,
+ line: 4,
+ messageId: 'noFunctionSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ declare function getFunction(): () => void;
+
+ const o = { ...getFunction() };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 37,
+ line: 4,
+ messageId: 'noFunctionSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ declare const f: () => void;
+
+ const o = { ...f };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 25,
+ line: 4,
+ messageId: 'noFunctionSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ declare const f: () => void | { a: number };
+
+ const o = { ...f };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 25,
+ line: 4,
+ messageId: 'noFunctionSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ function* generator() {}
+
+ const o = { ...generator };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 33,
+ line: 4,
+ messageId: 'noFunctionSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ const iterator = {
+ *[Symbol.iterator]() {
+ yield 'test';
+ },
+ };
+
+ const o = { ...iterator };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 32,
+ line: 8,
+ messageId: 'noIterableSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ type CustomIterable = {
+ [Symbol.iterator]: () => Generator;
+ };
+
+ const iterator: CustomIterable = {
+ *[Symbol.iterator]() {
+ yield 'test';
+ },
+ };
+
+ const a = { ...iterator };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 32,
+ line: 12,
+ messageId: 'noIterableSpreadInObject',
+ },
+ ],
+ options: [{ allow: ['AnotherIterable'] }],
+ },
+ {
+ code: `
+ declare module 'module' {
+ export type CustomIterable = {
+ [Symbol.iterator]: () => string;
+ };
+ }
+
+ import { CustomIterable } from 'module';
+
+ declare const iterator: CustomIterable;
+
+ const a = { ...iterator };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 32,
+ line: 12,
+ messageId: 'noIterableSpreadInObject',
+ },
+ ],
+ options: [
+ {
+ allow: [{ from: 'package', name: 'Nothing', package: 'module' }],
+ },
+ ],
+ },
+ {
+ code: `
+ declare const iterator: Iterable;
+
+ const o = { ...iterator };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 32,
+ line: 4,
+ messageId: 'noIterableSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ declare const iterator: Iterable | { a: number };
+
+ const o = { ...iterator };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 32,
+ line: 4,
+ messageId: 'noIterableSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ declare function getIterable(): Iterable;
+
+ const o = { ...getIterable() };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 37,
+ line: 4,
+ messageId: 'noIterableSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ class A {
+ [Symbol.iterator]() {
+ return {
+ next() {
+ return { done: true, value: undefined };
+ },
+ };
+ }
+ }
+
+ const a = { ...new A() };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 31,
+ line: 12,
+ messageId: 'noIterableSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ const o = { ...new Date() };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 34,
+ line: 2,
+ messageId: 'noClassInstanceSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ declare class HTMLElementLike {}
+ declare const element: HTMLElementLike;
+ const o = { ...element };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 31,
+ line: 4,
+ messageId: 'noClassInstanceSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ declare const regex: RegExp;
+ const o = { ...regex };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 29,
+ line: 3,
+ messageId: 'noClassInstanceSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ class A {
+ a = 1;
+ public b = 2;
+ private c = 3;
+ protected d = 4;
+ static e = 5;
+ }
+
+ const o = { ...new A() };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 31,
+ line: 10,
+ messageId: 'noClassInstanceSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ class A {
+ a = 1;
+ }
+
+ const a = new A();
+
+ const o = { ...a };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 25,
+ line: 8,
+ messageId: 'noClassInstanceSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ class A {
+ a = 1;
+ }
+
+ declare const a: A;
+
+ const o = { ...a };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 25,
+ line: 8,
+ messageId: 'noClassInstanceSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ class A {
+ a = 1;
+ }
+
+ declare function getA(): A;
+
+ const o = { ...getA() };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 30,
+ line: 8,
+ messageId: 'noClassInstanceSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ class A {
+ a = 1;
+ }
+
+ declare function getA(arg: T): T;
+
+ const o = { ...getA() };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 30,
+ line: 8,
+ messageId: 'noClassInstanceSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ class A {
+ a = 1;
+ }
+
+ class B extends A {}
+
+ const o = { ...new B() };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 31,
+ line: 8,
+ messageId: 'noClassInstanceSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ class A {
+ a = 1;
+ }
+
+ declare const a: A | { b: string };
+
+ const o = { ...a };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 25,
+ line: 8,
+ messageId: 'noClassInstanceSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ class A {
+ a = 1;
+ }
+
+ declare const a: A & { b: string };
+
+ const o = { ...a };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 25,
+ line: 8,
+ messageId: 'noClassInstanceSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ class A {}
+
+ const o = { ...A };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 25,
+ line: 4,
+ messageId: 'noClassDeclarationSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ const A = class {};
+
+ const o = { ...A };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 25,
+ line: 4,
+ messageId: 'noClassDeclarationSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ class Declaration {
+ declaration?: boolean;
+ }
+ const Expression = class {
+ expression?: boolean;
+ };
+
+ declare const either: typeof Declaration | typeof Expression;
+
+ const o = { ...either };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 30,
+ line: 11,
+ messageId: 'noClassDeclarationSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ const A = Set;
+
+ const o = { ...A };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 25,
+ line: 4,
+ messageId: 'noClassDeclarationSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: `
+ const a = {
+ ...class A {
+ static value = 1;
+ nonStatic = 2;
+ },
+ };
+ `,
+ errors: [
+ {
+ column: 11,
+ endColumn: 12,
+ endLine: 6,
+ line: 3,
+ messageId: 'noClassDeclarationSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: noFormat`
+ const a = { ...(class A { static value = 1 }) }
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 54,
+ line: 2,
+ messageId: 'noClassDeclarationSpreadInObject',
+ },
+ ],
+ },
+ {
+ code: noFormat`
+ const a = { ...new (class A { static value = 1; })() };
+ `,
+ errors: [
+ {
+ column: 21,
+ endColumn: 61,
+ line: 2,
+ messageId: 'noClassInstanceSpreadInObject',
+ },
+ ],
+ },
+ ],
+});
diff --git a/packages/eslint-plugin/tests/schema-snapshots/no-misused-spread.shot b/packages/eslint-plugin/tests/schema-snapshots/no-misused-spread.shot
new file mode 100644
index 000000000000..09a7e195ef81
--- /dev/null
+++ b/packages/eslint-plugin/tests/schema-snapshots/no-misused-spread.shot
@@ -0,0 +1,137 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Rule schemas should be convertible to TS types for documentation purposes no-misused-spread 1`] = `
+"
+# SCHEMA:
+
+[
+ {
+ "additionalProperties": false,
+ "properties": {
+ "allow": {
+ "description": "An array of type specifiers that are known to be safe to spread.",
+ "items": {
+ "oneOf": [
+ {
+ "type": "string"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "from": {
+ "enum": ["file"],
+ "type": "string"
+ },
+ "name": {
+ "oneOf": [
+ {
+ "type": "string"
+ },
+ {
+ "items": {
+ "type": "string"
+ },
+ "minItems": 1,
+ "type": "array",
+ "uniqueItems": true
+ }
+ ]
+ },
+ "path": {
+ "type": "string"
+ }
+ },
+ "required": ["from", "name"],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "from": {
+ "enum": ["lib"],
+ "type": "string"
+ },
+ "name": {
+ "oneOf": [
+ {
+ "type": "string"
+ },
+ {
+ "items": {
+ "type": "string"
+ },
+ "minItems": 1,
+ "type": "array",
+ "uniqueItems": true
+ }
+ ]
+ }
+ },
+ "required": ["from", "name"],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "from": {
+ "enum": ["package"],
+ "type": "string"
+ },
+ "name": {
+ "oneOf": [
+ {
+ "type": "string"
+ },
+ {
+ "items": {
+ "type": "string"
+ },
+ "minItems": 1,
+ "type": "array",
+ "uniqueItems": true
+ }
+ ]
+ },
+ "package": {
+ "type": "string"
+ }
+ },
+ "required": ["from", "name", "package"],
+ "type": "object"
+ }
+ ]
+ },
+ "type": "array"
+ }
+ },
+ "type": "object"
+ }
+]
+
+
+# TYPES:
+
+type Options = [
+ {
+ /** An array of type specifiers that are known to be safe to spread. */
+ allow?: (
+ | {
+ from: 'file';
+ name: [string, ...string[]] | string;
+ path?: string;
+ }
+ | {
+ from: 'lib';
+ name: [string, ...string[]] | string;
+ }
+ | {
+ from: 'package';
+ name: [string, ...string[]] | string;
+ package: string;
+ }
+ | string
+ )[];
+ },
+];
+"
+`;
diff --git a/packages/rule-tester/tests/flat-config-schema.test.ts b/packages/rule-tester/tests/flat-config-schema.test.ts
index 86356f99b4e8..b0131260d5ed 100644
--- a/packages/rule-tester/tests/flat-config-schema.test.ts
+++ b/packages/rule-tester/tests/flat-config-schema.test.ts
@@ -16,7 +16,7 @@ describe('merge', () => {
expect(result).toEqual({ ...first, ...second });
});
- it('returns an emtpy object if both values are undefined', () => {
+ it('returns an empty object if both values are undefined', () => {
const result = merge(undefined, undefined);
expect(result).toEqual({});
diff --git a/packages/typescript-eslint/src/configs/all.ts b/packages/typescript-eslint/src/configs/all.ts
index cd16389445a5..4bf1a3b4c5d4 100644
--- a/packages/typescript-eslint/src/configs/all.ts
+++ b/packages/typescript-eslint/src/configs/all.ts
@@ -87,6 +87,7 @@ export default (
'@typescript-eslint/no-meaningless-void-operator': 'error',
'@typescript-eslint/no-misused-new': 'error',
'@typescript-eslint/no-misused-promises': 'error',
+ '@typescript-eslint/no-misused-spread': 'error',
'@typescript-eslint/no-mixed-enums': 'error',
'@typescript-eslint/no-namespace': 'error',
'@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error',
diff --git a/packages/typescript-eslint/src/configs/disable-type-checked.ts b/packages/typescript-eslint/src/configs/disable-type-checked.ts
index 8ab7a8df71f6..838f00e62c29 100644
--- a/packages/typescript-eslint/src/configs/disable-type-checked.ts
+++ b/packages/typescript-eslint/src/configs/disable-type-checked.ts
@@ -32,6 +32,7 @@ export default (
'@typescript-eslint/no-implied-eval': 'off',
'@typescript-eslint/no-meaningless-void-operator': 'off',
'@typescript-eslint/no-misused-promises': 'off',
+ '@typescript-eslint/no-misused-spread': 'off',
'@typescript-eslint/no-mixed-enums': 'off',
'@typescript-eslint/no-redundant-type-constituents': 'off',
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off',
diff --git a/packages/typescript-eslint/src/configs/strict-type-checked-only.ts b/packages/typescript-eslint/src/configs/strict-type-checked-only.ts
index ef29e1006bc4..4d424ec0968f 100644
--- a/packages/typescript-eslint/src/configs/strict-type-checked-only.ts
+++ b/packages/typescript-eslint/src/configs/strict-type-checked-only.ts
@@ -35,6 +35,7 @@ export default (
'@typescript-eslint/no-implied-eval': 'error',
'@typescript-eslint/no-meaningless-void-operator': 'error',
'@typescript-eslint/no-misused-promises': 'error',
+ '@typescript-eslint/no-misused-spread': 'error',
'@typescript-eslint/no-mixed-enums': 'error',
'@typescript-eslint/no-redundant-type-constituents': 'error',
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error',
diff --git a/packages/typescript-eslint/src/configs/strict-type-checked.ts b/packages/typescript-eslint/src/configs/strict-type-checked.ts
index e5c7c4c22062..8753687ce006 100644
--- a/packages/typescript-eslint/src/configs/strict-type-checked.ts
+++ b/packages/typescript-eslint/src/configs/strict-type-checked.ts
@@ -49,6 +49,7 @@ export default (
'@typescript-eslint/no-meaningless-void-operator': 'error',
'@typescript-eslint/no-misused-new': 'error',
'@typescript-eslint/no-misused-promises': 'error',
+ '@typescript-eslint/no-misused-spread': 'error',
'@typescript-eslint/no-mixed-enums': 'error',
'@typescript-eslint/no-namespace': 'error',
'@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error',
diff --git a/packages/website/src/theme/NotFound/Content/index.tsx b/packages/website/src/theme/NotFound/Content/index.tsx
index 26990fa82a88..cbb83e4bdcc9 100644
--- a/packages/website/src/theme/NotFound/Content/index.tsx
+++ b/packages/website/src/theme/NotFound/Content/index.tsx
@@ -6,6 +6,10 @@ import styles from './styles.module.css';
export default function NotFound(): React.JSX.Element {
const location = useLocation();
+ // https://github.com/sindresorhus/eslint-plugin-unicorn/issues/2521
+ // eslint-disable-next-line @typescript-eslint/no-misused-spread
+ const pathNameQuoted = [...`'${location.pathname}'`];
+
return (
@@ -13,7 +17,7 @@ export default function NotFound(): React.JSX.Element {
$ npx eslint .
- {[...`'${location.pathname}'`].map((letter, i) => (
+ {pathNameQuoted.map((letter, i) => (
{letter}