88
function foo<T extends object>(t: T): T {

  return {
    ...t // Error: [ts] Spread types may only be created from object types.
  }
}

I am aware that there are issues on github, but I can't figure out what is fixed and what is not and they have 2695 open issues. So I am posting here. I am using latest Typescript 2.9.2.

Should the above code not work? And how can I fix it if possible?

Yves M.
  • 29,855
  • 23
  • 108
  • 144
Morten Poulsen
  • 1,163
  • 1
  • 8
  • 9
  • 3
    Looks like it fails to cast `t` to `object` correctly. Try `function foo(t: T): T { return { ...(t as object) } as T; }`, it's weird, but works – Jevgeni Jul 05 '18 at 11:04

6 Answers6

130

This is fixed in TypeScript Version 3.2. See Release Notes.


Looks like spread with a generic type isn't supported yet, but there is a GitHub issue about it: Microsoft/TypeScript#10727.

For now you can either use type assertion like @Jevgeni commented:

function foo<T extends object>(t: T): T {
  return { ...(t as object) } as T;
}

or you can use Object.assign which has proper type definitions.

function foo<T extends object>(t: T): T {
  return Object.assign({}, t);
}
jmattheis
  • 10,494
  • 11
  • 46
  • 58
  • 6
    You know what, as `tslint` say: `[tslint] Use the object spread operator instead.` :'( – Clite Tailor Oct 25 '18 at 07:28
  • 3
    @CliteTailor So change the lint rule if you don't like. Linters are supposed to help you establish certain rules, not stop you from writing things you want to write. – Lazar Ljubenović Jun 01 '19 at 16:18
  • 7
    Typescript 4.2 and still facing the same. I cast `...(t as Record)` to pass the lint as suggested by the linter @@ – Han Apr 03 '21 at 05:51
40

You can use Record<string, unknown> or an interface like the below examples:

goodsArray.map(good => {
  return {
    id: good.payload.doc.id,
    ...(good.payload.doc.data() as Record<string, unknown>)
  };
});

or

goodsArray.map(good => {
  return {
    id: good.payload.doc.id,
    ...good.payload.doc.data() as Goods // 'Goods' is my interface name
  };
});
mikemaccana
  • 110,530
  • 99
  • 389
  • 494
Muhammad Mabrouk
  • 606
  • 9
  • 16
10

Version 3.2 of Typescript fixed this. The two PRs that improve handling of spread and rest parameters are:

You can try it out now using npm install typescript@3.2.

With 3.2 your code works as is.

Version 3.2 has been released on November 29th, 2018, you can read more about it here.

stefanobaghino
  • 11,253
  • 4
  • 35
  • 63
Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
6

If you are still getting this error post version 3.2 then you probably have a type error 'upstream' resulting in an object being unknown instead of an actual object. For instance if you have the spread expression { ...getData() } but your getData function has a compile error you may see this error.

So check for any compiler errors in your browser console because the final error may be misleading.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
0

I had this issue with react-hook-form

I just did this:

{...(register('fieldName') as unknown as Record<any, unknown>)}
mbourd
  • 35
  • 4
-1

This answer may help to fix the same problem while using angular and firestoe.

return { id: item.payload.doc.id, ...item.payload.doc.data()} as Employee

this on will throw he same error. for fixing you have to change like this one.

return { id: item.payload.doc.id, ...item.payload.doc.data() as Employee }

Aathil Ahamed
  • 460
  • 4
  • 16