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:
T extends object as an object type, perhaps this should be implemented to better support spread/rest operators in modern ecmascript syntax.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"_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.
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
}
Just linking this for reference:
https://stackoverflow.com/questions/52469942/deep-cloning-entity-instances-in-typescript?noredirect=1#comment91881873_52469942
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.
Most helpful comment
:extremely long sigh: