Scenario

The “doStuff” team at your company created a function for you to use

function doStuff({ x, y, z }: { x: number; y: number; z?: number }) {
  console.log({ x, y, z })
}

Then, you diligently call it in your code like this

const obj = { x: 1, y: 2, z: 3 }
doStuff(obj)

all fine and good

Later on, the optional “prop” is renamed

The doStuff team says, well, it’s not really a true “z coordinate”…it’s more of a “z-index” so they change the function definition to be

function doStuff({ x, y, zIndex }: { x: number; y: number; zIndex?: number }) {
  console.log({ x, y, zIndex })
}

Now you might have a bug

Since zIndex is optional, TypeScript will not warn you about zIndex not being supplied by your existing code. Typescript is also not bothered that you have this “rider” variable z that “does nothing” in this case.

const obj = { x: 1, y: 2, z: 3 }
doStuff(obj) // no typescript error, z is no longer used, zIndex is optional, and you don't get the behavior you want

But wait, why didn’t TypeScript catch it?

Here is the funny thing:

  • TypeScript DOES NOT catch this issue when you pass it via this separately created “obj” variable.

  • TypeScript DOES(!!!) catch this issue when you pass it directly to the function (as an “object literal”)

doStuff({ x: 1, y: 2, z: 3 }) // ERROR!
// Object literal may only specify known properties, and 'z' does not exist in type '{ x: number; y: number; zIndex?: number | undefined; }'.

Demo playground

https://www.typescriptlang.org/play/?#code/FAMwrgdgxgLglgewgAgCYIMozCEAKAb2QA8AaZAT3IC9kBfALmSOKYjAFsAjAUwCcA3JTadeg5NQD8I7v3oBKZsGTIoSAM4IANjwB0WhAHNCJclQkLgdYKEixEKdFhwgATCbKUaASQioexPRMLDJiQhSh-ELUvv7E0sjssnwKSipqEJo6+kYeZj5+AZbWNsBO2LiErACMpBGupNQMAMx08qUA9B1oCDzqqgCGMFAAFshwMGWYFW5VDLX1jS1tNl09fRAA5DCDw2MTwBnqOwhcAFYAvAQ1dQwNTa1Tzrjup2ftpUA

Footnote

I am certainly not the only person to come across this “topic/issue”, and people often come across it in different contexts.

My particular take away is to “be careful when you rename optional props”. Of course, changing any API comes with risks, but this one particularly :)

More resources

Typescript Deep Dive - “Freshness”

https://basarat.gitbook.io/typescript/type-system/freshness

Follow up

This post was given lots of downvotes in /r/typescript and sarcasm that “no duh, breaking changes are breaking”.

However, some helpful comments were made. Read on https://www.reddit.com/r/typescript/comments/1fk1rqe/be_careful_when_you_rename_an_optional_prop_in/

I think maybe people are distracted that I posited this as the “doStuff team” vs “you”, but keep in mind this could just be your left brain vs right brain in an actively developed codebase