You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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();
}
}