ts-pattern icon indicating copy to clipboard operation
ts-pattern copied to clipboard

Handling of undefined with P.when

Open robertherber opened this issue 1 year ago • 1 comments

I'm really happy to have started using ts-pattern, thanks @gvergnaud for creating it! Today I ran into a typing issue when using P.when with potentially undefined values, see this example:

type ClampDate = (args: { readonly min?: Dayjs, readonly max?: Dayjs, readonly date: Dayjs }) => Dayjs

const clampDate: ClampDate = ({ min, max, date }) => match({ min, max, date })
  .with({ min: P.when((_) => date.valueOf() < _.valueOf()) }, ({ min }) => min)
  .with({ max: P.when((_) => date.valueOf() > _.valueOf()) }, ({ max }) => max)
  .otherwise(() => date.startOf('day'))

The type I get inside the P.when callback recognizes the min and max values as always defined at this stage. I thought maybe the callback is only called for defined values - until I ran into a runtime crash because of undefined values.

I notice the same is not the case for nullable types, here I will get a TypeScript error so I can ensure I don't run into runtime crashes:

type ClampDate = (args: { readonly min?: Dayjs | null, readonly max?: Dayjs | null, readonly date: Dayjs }) => Dayjs

const clampDate: ClampDate = ({ min, max, date }) => match({ min, max, date })
  .with({ min: P.when((_) => date.valueOf() < _?.valueOf()) }, ({ min }) => min)
  .with({ max: P.when((_) => date.valueOf() > _?.valueOf()) }, ({ max }) => max)
  .otherwise(() => date.startOf('day'))

robertherber avatar Jul 20 '22 20:07 robertherber

Thanks for the report, this definitely looks like a bug. P.when will be called if the key is defined on the input object, but since object types with optional properties allow assigning these properties to undefined, the key can be defined, but empty:

type Obj = { prop?: string }

const input: Obj = { prop: undefined } // typescript is ok with this.

match(input)
   .with({ prop: P.when(v => v /* v is undefined but has type string. v should have type `string | undefined` */) }, ...)

I'll have a look

gvergnaud avatar Aug 12 '22 08:08 gvergnaud

Repro case: https://www.typescriptlang.org/play?jsx=0#code/JYWwDg9gTgLgBAbziAhjAxgCwDRwApwC+cAZlBCHAOQwDOAtGGjAKZQB2VAUKJLIgBEUATwBWtYmQrUAJiPHcuS9BHa146ADYpwQ1nAC8cABRIQwdrlQAPXHP2EAXHERwoLFDNWbhyCwH5nITFaXHdPb18bQLhg8TCPL3YfOHsWIPlaIgBKQwA+LhdUDExTP0tkFFtUtBYcwrgAOgB3YBhSswtnPBbMFnZjYwB9XIM8mtZGgDcUTQBXFgB5EmNcgB44If9p2YXl1dzCXDLzdhz88uyGlraOyutu3v7BkYu0nfmlldzxrY+976HY5mKrnMb3K4uRoQdpsVq0FiDUbjd7qFCwfZUOTCKjZbJAA

gvergnaud avatar Feb 19 '23 18:02 gvergnaud

Fixed in v5!

gvergnaud avatar Jun 15 '23 18:06 gvergnaud