To your second question, using ===
for comparing a type with a single value is imho good practice in TypeScript. There are other ways to check for certain types which are more involved and overkill for this particular use, but there's a good blog post about it here. So to your first question, what is the standard way?
There is a type Option, defined in prelude-ts, which you might consider canonical. It has the definition:
type Option<T> = Some<T> | None<T>;
The type of your function would be:
function f(): Option<number> {}
Note that the extra <T>
for None
allows type reasoning to distinguish between, say, no string and no number.
In your examples you are using undefined
for None<T>
. Aside from the above, this has the drawback of giving less type safety when comparing values that are statically both known to be undefined
. If you don't care about this, and insist on using a JavaScript value for None
, consider using null
instead of undefined
, this at least gives you the advantage of being able to distinguish between uninitialised values and null
values on a type level:
if(x === undefined){
// succeeds if x is of type 'SomeT | undefined' but is undeclared
}
if(x === null){
// type error for reading x, if the type of x is 'SomeT | null' and x is undeclared
// success if x is null.
}
Which of the discussed options is best? Mathematically, the only thing equal to the maybe monad is the one as in prelude-ts
: it is the only definition that allows you to distinguish no-string from no-number on a type level.