You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The following code is deemed type-safe by TypeScript:
// Without the IIFE, Typescript restricts the type to null, even with an explicit typeconstuser=((): {name: string}|null=>null)();if(O.isSome(user)){console.log(user.name);}
However, it will throw at runtime. The IIFE simulates a Prisma select query, which returns a nullable value.
While this code is faster, it assumes that the Option was constructed using the library's constructors, such as O.fromNullable(), which converts null to undefined:
Unlike Result, Option is just a union with primitive types. TypeScript will implicitly cast a User | null to an Option<User>, which is an alias for User | null | undefined, allowing it to be used as the parameter of the isSome and isNone functions, causing the runtime error.
Solution
It's possible to use isNull() or isObject() in the guards module instead, however, isSome() and isNone() are still unsafe type predicates. They accept null values but do not check them (for the reasons stated above). I understand that they shouldn't be used in those contexts, but they can be used, and I'm worried that a colleague will make the same mistake and cause unexpected runtime errors that are difficult to track down that could easily have been avoided by checking for null and a negligible performance impact...
Personally, I would recommend removing undefined from the None type. null has a semantic value meaning "nothing", while undefined signifies more than the parameter or variable has not been set. This does not have a performance impact, and undefined can be removed from Some. I'm not familiar with Rescript, but unfortunately, it seems to me that undefined is hardcoded as the TS equivalent of None.
As an extension to the above, most functions in the Option module will cause runtime errors when null is involved, which is often the case with Prisma. For example, the following code:
causes a runtime error TypeError: Cannot read properties of null (reading 'name'). Again, TypeScript assumes that null is a valid type for None, however, ts-belt makes the assumption that an Option can only be undefined.
Example
The following code is deemed type-safe by TypeScript:
However, it will throw at runtime.
The IIFE simulates a Prisma select query, which returns a nullable value.
Explanation
None
is defined as eithernull
orundefined
:https://github.com/mobily/ts-belt/blob/c825d9709de1e5d15b1b362429e0cee56c96c516/src/Option/index.ts#L3C1-L6C47
However, the generated code for the
isSome()
andisNone()
type guards only checks for theundefined
case:While this code is faster, it assumes that the
Option
was constructed using the library's constructors, such asO.fromNullable()
, which convertsnull
toundefined
:Unlike
Result
,Option
is just a union with primitive types. TypeScript will implicitly cast aUser | null
to anOption<User>
, which is an alias forUser | null | undefined
, allowing it to be used as the parameter of theisSome
andisNone
functions, causing the runtime error.Solution
It's possible to use
isNull()
orisObject()
in the guards module instead, however,isSome()
andisNone()
are still unsafe type predicates. They acceptnull
values but do not check them (for the reasons stated above). I understand that they shouldn't be used in those contexts, but they can be used, and I'm worried that a colleague will make the same mistake and cause unexpected runtime errors that are difficult to track down that could easily have been avoided by checking fornull
and a negligible performance impact...Personally, I would recommend removing
undefined
from theNone
type.null
has a semantic value meaning "nothing", whileundefined
signifies more than the parameter or variable has not been set. This does not have a performance impact, andundefined
can be removed fromSome
. I'm not familiar with Rescript, but unfortunately, it seems to me thatundefined
is hardcoded as the TS equivalent ofNone
.The text was updated successfully, but these errors were encountered: