Closed
Description
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.