TypL
TypL copied to clipboard
Union/Polymorphic Types
Need to be able to define a union type that can include multiple types.
So... I'm imagining something like this:
var x = (union`int | string`)`42`;
Note: The ( )
are optional but useful for readability. These would work too:
union`int | string``42`; // no space between
union`int | string` `42`; // space between
Function Parameter Notation
Used for a function parameter:
function foo(x = union`int | string`) {
// ..
}
empty
Union Type
It's useful to combine null
and undefined
into a union type to allow a variable to hold either of those "empty" values:
var x = (union`undef | nul`)`null`;
x = undefined; // OK
x = null; // OK
x = 3; // error!
So empty
would just be a good union-type example to just build in... a type that already includes both undef
and nul
. It's trivial to define:
var empty = union`undef | null`;
And easy to use:
var x = empty``;
x; // undefined
x = empty`null`;
x; // null
This empty
union type would be especially useful for "optional" annotations, as another union type, like:
function foo(x = union`int | empty`) {
// ..
}
Union Order Matters
The order that types are listed in the union matters, for the purposes of parsing a literal and evaluating it as a type, since the processing is left to right and the first successful type evaluation "wins". In general, list types from left to right in order of most specific to most general/accepting. IOW: string
, being the most liberally accepting, should be listed last, or otherwise no other types after it will ever even be evaluated for that literal.
Example:
(union`int | string`)`42`; // 42
(union`string | int`)`42`; // "42"
Of course, if no parsing is involved, order is irrelevant (but still a good idea to follow the above rules):
var x = 42;
(union`int | string`)`${x}`; // 42
(union`string | int`)`${x}`; // 42
Union Collapsing
A special case of union types is ones which should be collapsed, like number | finite
, as that should just collapse to number
. finite | int
should collapse to finite
. empty | nul
should collapse to empty
.
This would replace (or fit with) the "number sub-types" handling of #6.
Implementation
The way this works in runtime mode is that union()
is a special tag function that produces another tag function which then runs against the subsequent template literal (for example, `hello`
or `42`
). The resulting tag function would allow any value that successfully passes either/any of the referenced tag functions, and error otherwise.
Rough-draft implementation:
function union([str]) {
var typeFns = str
.split(/\s+\|\s+/g)
.map(s => {
try { return Function(`return ${s.trim()};`)(); } catch (e) {}
})
.filter(Boolean);
return function tag(...args) {
var errs = [];
for (let fn of typeFns) {
try {
return fn(...args);
}
catch (e) {
errs.push(e.toString());
}
}
throw new Error(`Invalid union-type value: ${errs.join(" ")}`)
};
}
Illustrating its operation:
(union`int | string`)`hello`; // "hello"
(union`int | string`)`42`; // 42
(union`int | string`)`${true}`; // error because bool is not in the union type
Polymorphic Types
The same union-types mechanism will be handle "polymorphic types" which are types that change throughout the program. There are a number of constructs that would implicitly "widen" a type to such a union type.
Example:
var a = undef``;
a = nul``;
The second assignment here will report an error (if you config it) about an unexpected assignment type, but ALSO now the type in a
will be a union type of undef | nul
(which might collapse to empty
), since a
now potentially holds either value during the program.
Another example:
var x = int`0`;
var y = string`foo`;
var z = x || y;
Again, if you config it, the x || y
will produce an error about mixed operand types. But the resulting type that will be implied to z
will be a union type of int | string
.
And:
function foo(x = number) {
if (x > 5) return int`${x}`;
return "too small";
}
Error message (if config'd) about multiple return types, but also the return value in that function signature will be the union type of int | string
.
Hi Kyle I would like to help