Home > Software design >  Typescript warning when spreading optional keys
Typescript warning when spreading optional keys

Time:03-19

Im trying to understand why I get a compiler warning from Typescript when trying to create a new object based on an existing object of the same type.

In the code below you can see that spreading the key2 value causes the error Type 'string | undefined' is not assignable to type 'string'.. Although key2 is optional, i'm spreading the value from object1 which we know has a key2 entry.

type Test = {
  key1: string;
  key2?: {
    key3: string;
  }
}

const object1:Test = {
  key1: "hello",
  key2: {
    key3: "hi"
  }
}

const object2:Test = {
  ...object1,
  key2: { // error here
    ...object1.key2
  }
}

Type error:

Type '{ key3?: string | undefined; }' is not assignable to type '{ key3: string; }'.
  Types of property 'key3' are incompatible.
    Type 'string | undefined' is not assignable to type 'string'.
      Type 'undefined' is not assignable to type 'string'.(2322)

I'm just interested in understanding the behaviour here and the best way to handle this sort of situation.

TS Playground link: https://www.typescriptlang.org/play?#code/C4TwDgpgBAKhDOwoF4oG8BQUoGsIgEYAuKRAJwEsA7AcwG4td8AmAfhM22zxAGYTy1eowC GMRgDGAeyqIo0gEYArCJODE481JyaESAIgAWEADanpBgDSMezDo275 UYxQOjxGKbPlLV6vZaSDqMAHQR-moaNk4g9uiOUBFhUeoEYXaeIkA

Thanks

CodePudding user response:

The type of key2 is:

key2?: {
  key3: string;
}

That means key2 is:

  • allowed to be absent
  • allowed to be undefined
  • allowed to be { key3: string } (an object with key3 property that is is a string.)

What's not allowed is an object that has an optional key3 property.

But if we look at the type of your spread:

const a = { ...object1.key2 }
// { key3?: string | undefined; }

That's not a valid assignment of the key2 property because if its an object it must also have a key3 property with a string value.


To satisfy this interface, you need to prevent creating an object if key2 is undefined.

One way to do that would be:

const object2:Test = {
  ...object1,
  key2: object1.key2 && {
    ...object1.key2
  }
}

Playground

  • Related