Description
Maybe it's the Haskeller in me, but since we have had type aliases, I've wanted to be able to specify type Foo<T> = ...
in aliases. I offer a few motivating examples:
First, The d3.js API accepts constant values or functions that compute values (d3.functor):
type Functor<T> = T | (() => T);
interface Selection {
attr(name: string, value: Functor<string>): Selection;
}
Second, objects-as-hashmaps of some kind (often seen as initializers for keyed collections):
type Bag<T> = {
[key: string]: T;
};
type List<T> = {
[key: number]: T;
};
(Indeed, we could extrapolate to an Iterable<T>
type, which would make sense for things like underscore's collection functions:)
type Iterable<T> = Bag<T> | List<T>;
The biggest use case I've found is promises, whose APIs often accept T
or a promise-for-T
:
type MaybePromise<T> = T | Promise<T>;
export function when<T>(value: MaybePromise<T>): Promise<T>;
Parameterized interfaces can solve a few cases, but the addition of unions means that some types have to be written out by hand. Promises get especially hairy as higher-level APIs may have nested containers of promises or values (such as, to name a random example, when.map).
The best I could do for the type of when.map
is this:
export function map<T, U>(
array: Promise<Array<T | Promise<T>>> | Array<T | Promise<T>>,
func: (val: T, index: number) => U | Promise<U>
): Promise<U[]>
Most people would probably write this as two functions, to save on having to count angle brackets:
export function map<T, U>(
array: Array<T | Promise<T>>,
func: (val: T, index: number) => U | Promise<U>
): Promise<U[]>
export function map<T, U>(
array: Promise<Array<T | Promise<T>>>,
func: (val: T, index: number) => U | Promise<U>
): Promise<U[]>
But both definitions can be collapsed given the MaybePromise
alias above:
export function map<T, U>(
array: MaybePromise<Array<MaybePromise<T>>>,
func: (val: T, index: number) => MaybePromise<U>
): Promise<U[]>
Admittedly, the type definition for when.map
will never win beauty prizes, but the conciseness seems useful, if only to spare the fingers of DefinitelyTyped users.
This will probably need a proper spec, but I'm hoping that my intent is clear. It's something of an open question if this gives the type alias feature too much power, but I think that it can be extremely useful when used responsibly.