I want to map custom type classes and expect them to work seemlessly with TypeScript’s Mapped Types.
The issue is that when I use a callback, the result will be expanded(?) to a union, which I don’t want.
I believe the code speaks for itself
const any = (any?: any) => any;
// CONSTRAINTS
namespace Constraint {
export type OBJ = { [K: string]: ANY };
export type OPT = ANY;
// @ts-ignore // I know how to fix this, but please ignore for now! Thank you :)
export type ANY = Str | Num | Obj<OBJ> | Opt<ANY>;
}
// TYPES
type Nested<T extends { nested: unknown }> = T['nested'];
type Opt<T extends Constraint.OPT> = OPT<T>;
type Str = STR;
type Num = NUM;
type Obj<T extends Constraint.OBJ> = OBJ<T>;
// CLASSES
class OPT<T extends Constraint.OPT> {
opt: undefined;
constructor(public nested: T) {}
}
class OBJ<T extends Constraint.OBJ> {
obj: undefined;
public map<R extends Constraint.ANY>(
cb: (value: Nested<this>[keyof Nested<this>]) => R
): Obj<{
[K in keyof Nested<this>]: R;
}> {
return any(cb);
}
constructor(public nested: T) {}
}
class STR {
str: undefined;
}
class NUM {
num: undefined;
}
// FUNCTIONS
const Opt = <T extends Constraint.OPT>(nested: T): Opt<T> => new OPT<T>(nested);
const Obj = <T extends Constraint.OBJ>(nested: T): Obj<T> => new OBJ<T>(nested);
const Str = (): Str => new STR();
const Num = (): Num => new NUM();
// USE
const example = Obj({
one: Str(),
two: Num(),
});
export const result = example.map((value) => Opt(value)); // => See below:
I get this:
Obj<{
one: Opt<STR | NUM>;
two: Opt<STR | NUM>;
}>
But I really just want this:
Obj<{
one: Opt<STR>;
two: Opt<NUM>;
}>
Playground
Would I need HKTs (Higher-Kinded Types) for this, as described here, or can I solve it another way?
2