Typescript: Spread operator fails for object type generics

Created on 13 Aug 2018  路  7Comments  路  Source: microsoft/TypeScript

TypeScript Version: 3.0

Search Terms:
generic, spread, object types, spread types, spread operator

Code

function shallowCopy<T extends object>(state: T): T {
  return {
    ...state
  }
}

Expected behavior:
Should compile as only state's properties are being shallow-copied to the returned result and thus meets the T type.

Actual behavior:
Does not compile with the following error message:

> Spread types may only be created from object types.

If T extends object, is it not then an object type? If not, what type or interface can be defined that is an object type and is not exactly {} or object?

Possible ways forward:

  • If the compiler does not properly recognize T extends object as an object type, perhaps this should be implemented to better support spread/rest operators in modern ecmascript syntax.
  • If T extends object is a recognized object type, the compiler error should be amended to something like _"Spread types may only be created from the object type in generics"_
  • Silently lock and close this issue adding documentation that states _"The spread operator does not support generics extending object"_

Playground Link: Contrived example

Related Issues:
https://github.com/Microsoft/TypeScript/pull/13288 - While this seems like the use case was addressed by @RyanCavanaugh , the resulting workaround suggests far more explicit typing than should be required by the compiler to infer the types in the example provided. While closing that PR might have been the protocol, it seemed more effectively to silence the community request for spread operator support with generics extending from object.

Bug Error Messages help wanted

Most helpful comment

Silently lock and close this issue

:extremely long sigh:

All 7 comments

Duplicate of #24060, #24630, #22687, #20510, #20013.
I'll mark this as an error message bug since we should be providing a message that this doesn't and likely never will work for a type parameter without a cast.

Just digging through those links, https://github.com/Microsoft/TypeScript/issues/10727#issuecomment-306562488 suggests that there wasn't a specific reason for this to not work originally, but by https://github.com/Microsoft/TypeScript/issues/20013 it was decided that this was correct to reject this.

On the one hand, the reasoning there, essentially that own/enumerable requirement for spread isn't modeled by TS, is also an issue even without generics so allowing this usage isn't introducing a new type-safety problem; on the other, the problem is much larger with generics, as the heuristic TS uses for non-generic spreads (which is just strip methods, right?) doesn't work. Is this why #10727 was recently tagged as Revisit?

Came here because of:

type A = {
    a: string
    b: number
}

function f<B extends object>(x: A & B) {
    const {a, b, ...other} = x // does not work
}

Silently lock and close this issue

:extremely long sigh:

Reading #20013, I understand that ...T is not assignable T. But may I know the challenge for getting a correctish type inference?

For example:

type MyObj = {
  a: string,
  b: number,
  c: boolean,
}

function foo<T extends MyObj>(value: T) {
  const { a, ...otherValues } = value;
}

is it possible to infer otherValues as Omit<MyObj, 'a'> & AnyObject, so we can at least make it type-safe if we access properties in otherValues that belongs to MyObj?

Regarding #20013, is it possible to have a helper type RemovePrototype<T> or something?

This bug is probably related to #8856 somehow.

Was this page helpful?
0 / 5 - 0 ratings