Skip to content

Type aliases requiring type parameters #1616

Closed
@danielearwicker

Description

@danielearwicker

Sometimes we need to capture a union of types that may be a mixture of primitives, functions, objects, etc. but we want to leave it parameterised (just as we already can with interfaces).

Example

A simple non-recursive example:

type Source<T> = T | (() => T);

Here, a source can either be a plain value or a nullary function that obtains a value.

We can provide uniform access to such sources:

function unwrap<T>(p: Source<T>) {
    return (typeof p === "function") ? p() : p;
}

And then we can specify model interfaces where we we leave open the nature of the source but we tie down the value types:

interface Person {
    name: Source<string>;
    age: Source<number>;
}

e.g. name is a constant, but age depends on when you ask:

var p: Person = {
    name: "John Lennon",
    age: () => ageFromDOB(1940, 10, 9),
}

But we can treat them identically in consuming code:

var n = unwrap(p.name), a = unwrap(p.age);

NB. The above is already possible with union types alone, but the interface Person has to repeat the pattern:

interface Person {
    name: string | (() => string);
    age: number | (() => number);
}

Not so bad for a simple example, but the pattern for a value source might evolve to get more complex and then you have a lot of fiddly updating to do because you "Did Repeat Yourself".

Recursion

The more flexible recursive version:

type Source<T> = T | (() => Source<T>);

A source can either be a plain value or a nullary function that obtains a source (which may be a plain value terminating recursion, or a nullary function that... and so on).

We can again provide uniform access to such sources, either with runtime recursion (risky until tail-call optimisation is widespread):

function unwrap<T>(p: Source<T>) {
    return (typeof p === "function") ? unwrap(p()) : p;
}

Or with a loop:

function unwrap<T>(p: Source<T>) {
    for (;;) {
        if (typeof p !== "function") {
            return p;
        }
        p = p();
    }
}

Metadata

Metadata

Assignees

Labels

CommittedThe team has roadmapped this issueSuggestionAn idea for TypeScript

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions