Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parameterized type aliases #2155

Closed
gustavderdrache opened this issue Feb 26, 2015 · 1 comment
Closed

Parameterized type aliases #2155

gustavderdrache opened this issue Feb 26, 2015 · 1 comment
Labels
Duplicate An existing issue was already created

Comments

@gustavderdrache
Copy link

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.

@danquirk
Copy link
Member

Not just you, see #1616

@danquirk danquirk added the Duplicate An existing issue was already created label Feb 26, 2015
@microsoft microsoft locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

2 participants