Skip to content

Rule proposal: prevent array, iterable and function spreads into objects #748

Closed
@ckknight

Description

@ckknight

Repro

// Array spread
const squares = [1, 4, 9, 16];
const cubes = [1, 8, 27];
const bothArray = { ...squares, ...cubes };
// rule would error here, as spreading an array causes surprising results
bothArray === {0: 1, 1: 8, 2: 27, 3: 16}
// TS: typeof bothArray === (typeof Array<number>) ❌

// Iterable spread
const map = new Map([[1,2], [3,4]]);
const set = new Set([5,6,7,8]);
const bothIterable = { ...map, ...set };
// rule would error here as spreading an iterable does nothing
bothIterable === {}
// TS: typeof allFunc === (typeof Set<number> & typeof Map<number, number>) ❌

// Function spread
function funcDecl() {}
const funcExpr = function () {};
const arrowFunc = () => {};
const allFunc = { ...funcDecl, ...funcExpr, ...arrowFunc };
// rule would error here as spreading a function does nothing
allFunc === {};
// TS: typeof allFunc === ({}) ✅

// Object with call-signature
interface FuncWithProps {
  property?: string;
  (): number;
}
const funcWithProps: FuncWithProps = () => 1;
funcWithProps.property = 'foo';
const spreadFuncWithProps = {...funcWithProps};
// rule would NOT error here as custom function props are spread as expected
spreadFuncWithProps === {property: 'foo'};
// TS: typeof allFunc === (FuncWithProps) ✅

TypeScript does not have an understanding of how iterable object spreads work, and borks the types.
This leads to unsoundness and broken code.

TypeScript does understand function object spreads, but they are noops, so they shouldn't be allowed.

Additional Information
Detecting this should be a matter of detecting whether the type of each expression being spread within an object implements Iterable<T>, defined within lib.es2015.iterable.d.ts.

The converse rule of preventing objects being spread within an array seems unnecessary/out-of-scope, given that the TypeScript type system is able to determine that each expression spread within an array is iterable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    accepting prsGo ahead, send a pull request that resolves this issueenhancement: new plugin ruleNew rule request for eslint-pluginlocked due to agePlease open a new issue if you'd like to say more. See https://typescript-eslint.io/contributing.package: eslint-pluginIssues related to @typescript-eslint/eslint-plugin

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions