Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spread operator stripping type definition in typescript

Tags:

typescript

I ran into this strange phenomenon in typescript. When using the spread operator, the type checking seems to be lost. I would expect this to throw an error:

interface Input {
  thingOne: string;
  thingTwo: number;
  thingThree: string;
  thingFour: boolean;
  thingFive: Array<number>
}

interface Output {
  thingThree: string;
  thingFour: boolean;
  thingFive: Array<number>
}

const transformFunction = (input: Input): Output => {
  const result: Output = {
    ...input,
    thingFour: !input.thingFour
  }
  return result
}

Typescript playground example

I am typing result as Output, but spreading an object of type Input into it would clearly produce a result that is not of the type Output. Why is typescript not catching this?

like image 453
Seth Lutske Avatar asked Sep 16 '25 15:09

Seth Lutske


2 Answers

What we are expecting here is to trigger Excess Property Check feature of TypeScript. This feature tells us that TypeScript would warn us if there are extra properties in the object if we explicitly mention the object type. Unfortunately, it works only for object literals.

For example this:

const transformFunction = (input: Input): Output => {
  return {
    thingOne: "string;",
    thingTwo: 123,
    thingThree: "string;",
    thingFour: !input.thingFour,
    thingFive: [1, 23],
  };
};

Warns:

Type '{ thingOne: string; thingTwo: number; thingThree: string; thingFour: boolean; thingFive: number[]; }' is not assignable to type 'Output'.
  Object literal may only specify known properties, and 'thingOne' does not exist in type 'Output'.

It seems like spread operator case is not covered under exces property check. See here: https://github.com/microsoft/TypeScript/issues/41237

This is the excess property check. According to @RyanCavanaugh it's more of a linter, not really part of the type system. But not every case is covered by this check, as you noticed.

They also mentioned looking into ongoing conversion on exact types here: https://github.com/microsoft/TypeScript/issues/12936

like image 101
Nishant Avatar answered Sep 18 '25 09:09

Nishant


The function is type correct since type Input can be assigned to type Output without issues, even though it has extra properties. For literal objects only, there is an excess property check, because excess properties in a object literal are very likely to be mistakes. Consider this constant for example.

const o: {a: number} = {a: 1, b: 2}

As o now has type {a: number}, there is no typed way to access the b property, so TypeScript considers it an error.

like image 31
Oblosys Avatar answered Sep 18 '25 09:09

Oblosys