microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
101.08k stars 12.49k forks source link

Object spread works for unconstrained generic #38469

Open Methuselah96 opened 4 years ago

Methuselah96 commented 4 years ago

TypeScript Version: 3.8.3

Search Terms: generic spread unrestrained unrestricted

Expected behavior: The spread function should not compile because T is not constrained to a type that can be spread.

Actual behavior: It compiles.

Related Issues: https://github.com/microsoft/TypeScript/issues/30129 https://github.com/microsoft/TypeScript/pull/13288

Code

function spread<T>(t: T) {
  // This should not compile
  return { ...t };
}

// The type of `result` is `number` even though the value of `result` is `{}`
const result = spread(5);
Output ```ts "use strict"; function spread(t) { // This should not compile return Object.assign({}, t); } // Result is still a number when in reality it's {} const result = spread(5); ```
Compiler Options ```json { "compilerOptions": { "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictPropertyInitialization": true, "strictBindCallApply": true, "noImplicitThis": true, "noImplicitReturns": true, "alwaysStrict": true, "esModuleInterop": true, "declaration": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "moduleResolution": 2, "target": "ES2017", "jsx": "React", "module": "ESNext" } } ```

Playground Link: Provided

nmain commented 4 years ago

How much existing code would this break? I suspect there are a lot of generic spreads out there that were never intended to be used on primitives but at the same time don't have a useful generic bound on them.

Methuselah96 commented 4 years ago

But it also has the potential to fix types for existing code or provide some type-safety where there currently is none. If the generic spreads that you mention are not intended to be used for primitives then they should include a generic bound that disallows primitives.