Skip to content

Parameterized type aliases #2155

Closed
Closed
@gustavderdrache

Description

@gustavderdrache

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions