type Thing = string | boolean;
const arr: string[] = [];
function add(item: Thing, dst: Thing[]): void {
dst.push(item);
}
add(false, arr);
Is valid typescript. type Thing = string | boolean;
const arr: string[] = [];
function add(item: Thing, dst: Thing[]): void {
dst.push(item);
}
add(false, arr);
Is valid typescript.However, regularly it's not the case -- especially with people moving from nominal systems.
TS can't really be practically nominal when it has to be constrained by its compile target. So I guess it ultimately boils down to an anomaly/criticism born from the legacy of how web standards came about.
But yeah unfortunately not sure if JS is an appropriate target for these type systems. Nim does it, but I'm not sure how safe it is.
type thing =
| String(string)
| Bool(bool);
let arr: list(thing) = [];
let add = (item: thing, dst: list(thing)): list(thing) => {
switch (item) {
| String(s) => dst @ [String(s)]
| Bool(b) => dst @ [Bool(b)]
};
};
let newList = add(String("asd"), arr);
This doesn't work, since the dst array has to be one that can contain both string and bool in the first place.I understand it may be unwanted at first glance, but this is a contrived example for demo purposes. You wouldn't really make an "add to array" function like this so specifically. You would use generics, which would solve the exact issue that is posed.
function add<T>(item: T, dst: T[]): void {
dst.push(item);
}
Now you can work with any array, and it will only add items with the right type.If you really need it to be just <Thing>, you can do this
function add<T extends Thing>(item: T, dst: T[]): void {
dst.push(item);
}
Now it knows that Item and DST are related but need to be Thing.There's not a lot of languages with unions that handle this differently. F# discriminated unions make you specify which type you're using every time. For example, this doesn't even compile.
type Thing =
| A of int
| B of bool
let arr: Thing list = [1] // Not an A or B type!