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