Currently, I am working on creating a utility type to unwrap nested monads of Options in my code. Here is the progress I have made so far:
export interface Option<T> {
type: symbol;
isSome(): boolean;
isNone(): boolean;
match<U>(fn: Match<T, U>): U;
map<U>(fn: (val: T) => U): Option<U>;
andThen<U>(fn: (val: T) => Option<U>): Option<U>;
or<U>(optb: Option<U>): Option<T | U>;
and<U>(optb: Option<U>): Option<U>;
unwrapOr(def: T): T;
unwrap(): T | never;
}
export type UnwrappedOptionsType<T> = T extends (infer U)[]
? UnwrappedOptionsType<U>[]
: T extends object
? {
[P in keyof T]: T[P] extends Option<infer R>
? UnwrappedOptionsType<R> | undefined
: UnwrappedOptionsType<T[P]>;
}
: T;
My expectation is that the types will be inferred correctly and properties that are Options become optional. For example, consider the following type:
type SignUpRequest = {
username: string;
password: string;
email: Option<string>;
}
When using
UnwrappedOptionsType<SignUpRequest>
, expected output should be:
{
username: string;
password: string;
email?: string | undefined;
}
However, the actual result obtained is:
{
username: string;
password: string;
email: string;
}
The type of the option is successfully inferred, but it does not accept undefined
. How can I make these options optional?
Edit: Updated code for reproducibility. Also, the requirement is for properties to be explicitly optional, not just possibly undefined.