Mateusz Klinowski
Mateusz Klinowski

Reputation: 23

TypeScript difference between original object and spreaded object

there is a riddle about Typescript I cannot understand:

const  randomFn = (arg: Record<string, unknown>): string => 'kappa'

export type Values = {
    key: string;
};
const values: Values = {
    key: 'kappa'
}

const { ...spread } = values;

randomFn(values)
randomFn(spread) // produce Index signature for type 'string' is missing in type '{ transaction: string; }'.(2345)

Why is Typescript producing errors for spread object when in my understanding it typing should be the same. If we inspect the object in playgroud it is exactly the same for TS compiler. Both original and spreaded.

TS version 4.5.4. Reproduction in Type Script Playground

EDIT: also doing double spread to make it work again .

randomFn({...spread}) // NO error

Upvotes: 2

Views: 152

Answers (2)

jcalz
jcalz

Reputation: 329523

This is the result of a design limitation of TypeScript, see microsoft/TypeScript#42021 for details. When you use a rest element in an object destructuring assignment, TypeScript unfortunately does not copy an index signature into the type of the new variable. Nor does it allow the variable to have an implicit index signature (although I'm not sure why).

Your Values type alias is allowed to have an implicit index signature (if you made it an interface then it wouldn't either, see microsoft/TypeScript#15300), so it is considered assignable to Record<string, unknown> (which is equivalent to {[k: string]: unknown}, a type with a string index signature). But spread, as a rest-destructured variable, is not, and therefore it fails.

You noticed that doing a second rest destructuring does work, so presumably there's something weird about how object types get marked to allow or disallow implicit index signatures. This is also noted in microsoft/TypeScript#42021, although without explanation.

So unfortunately there's not a very satisfying answer to be had right now, unless more activity happens in microsoft/TypeScript#42021.

Upvotes: 0

tenshi
tenshi

Reputation: 26344

Spreading loses the type because it has no guarantee that the type of the spread object is the same. Consider this:

const { excluded, ...rest } = object;

Clearly rest can't be the same type as object; there is a key missing!

You can also think of it like this:

const [excluded, ...rest] = array;

That's why spread is not type Values.

Upvotes: 1

Related Questions