Skip to main content

no-misused-spread

Disallow using the spread operator when it might cause unexpected behavior.

💭

This rule requires type information to run.

Spread syntax (...) is a JavaScript feature for creating an object with the joined properties of one or more other objects. TypeScript allows spreading objects whose properties are not typically meant to be enumerated, such as arrays and class instances.

This rule disallows using the spread syntax on values whose types indicate doing so may cause unexpected behavior. That includes the following cases:

  • Spreading a Promise into an object. You probably meant to await it.
  • Spreading a function without properties into an object. You probably meant to call it.
  • Spreading an iterable (Array, Map, etc.) into an object. Iterable objects usually do not have meaningful enumerable properties and you probably meant to spread it into an array instead.
  • Spreading a string into an array. String enumeration behaviors in JavaScript around encoded characters are often surprising.
  • Spreading a class into an object. This copies all static own properties of the class, but none of the inheritance chain.
  • Spreading a class instance into an object. This does not faithfully copy the instance because only its own properties are copied, but the inheritance chain is lost, including all its methods.
eslint.config.mjs
export default tseslint.config({
rules: {
"@typescript-eslint/no-misused-spread": "error"
}
});

Try this rule in the playground ↗

Examples

declare const promise: Promise<number>;
const spreadPromise = { ...promise };

declare function getObject(): Record<string, strings>;
const getObjectSpread = { ...getObject };

declare const map: Map<string, number>;
const mapSpread = { ...map };

declare const userName: string;
const characters = [...userName];
Open in Playground
declare class Box {
value: number;
}
const boxSpread = { ...Box };

declare const instance: Box;
const instanceSpread = { ...box };
Open in Playground

Options

This rule accepts the following options:

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
)[];
},
];

const defaultOptions: Options = [{ allow: [] }];

allow

An array of type specifiers that are known to be safe to spread. Default: [].

This option takes the shared TypeOrValueSpecifier format.

Examples of a configuration for this option in a file.ts file:

"@typescript-eslint/no-misused-spread": [
"error",
{
"allow": [
{ "from": "file", "name": "BrandedString", "path": "file.ts" },
]
}
]
declare const unbrandedString: string;

const spreadUnbrandedString = [...unbrandedString];
Open in Playground

When Not To Use It

If your application intentionally works with raw data in unusual ways, such as directly manipulating class prototype chains, you might not want this rule.

If your use cases for unusual spreads only involve a few types, you might consider using ESLint disable comments and/or the allow option instead of completely disabling this rule.

Further Reading


Type checked lint rules are more powerful than traditional lint rules, but also require configuring type checked linting.

See Troubleshooting > Linting with Type Information > Performance if you experience performance degradations after enabling type checked rules.

Resources