ts-pattern
ts-pattern copied to clipboard
Handling of undefined with P.when
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'))
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
Repro case: https://www.typescriptlang.org/play?jsx=0#code/JYWwDg9gTgLgBAbziAhjAxgCwDRwApwC+cAZlBCHAOQwDOAtGGjAKZQB2VAUKJLIgBEUATwBWtYmQrUAJiPHcuS9BHa146ADYpwQ1nAC8cABRIQwdrlQAPXHP2EAXHERwoLFDNWbhyCwH5nITFaXHdPb18bQLhg8TCPL3YfOHsWIPlaIgBKQwA+LhdUDExTP0tkFFtUtBYcwrgAOgB3YBhSswtnPBbMFnZjYwB9XIM8mtZGgDcUTQBXFgB5EmNcgB44If9p2YXl1dzCXDLzdhz88uyGlraOyutu3v7BkYu0nfmlldzxrY+976HY5mKrnMb3K4uRoQdpsVq0FiDUbjd7qFCwfZUOTCKjZbJAA
Fixed in v5!