One of the killer feature of ReScript is built-in support of discriminated unions and pattern match. TS has something close so that we can model discriminated unions in both languaguages without copying.
Here are some ideas to encode the discriminated unions in both languages without coping:
type data =
| A ({name : string})
| B ({value : number})
| C
| D
The corresponding TS encoding would be:
enum dataNonNull = { A, B }
enum dataNull = { C, D}
type data =
| { tag : dataNonNull.A, name : string}
| { tag : dataNonNull.B, name : string}
| dataNull.C
| dataNull.D
The use case is that when you export libraries written in ReScript to TypeScript.
I think this is a great idea. Only thing is I don’t think you need to split the enums up here, one should be okay, and TS will still do the type checking just fine.
Ah that’s a good point. I assumed you generated a .d.ts, you could just specify an enum exists. I guess you already tried that because TS complains if it isn’t told what the enum values are. The best I could do was lie,
.d.ts
// File.d.ts
export enum DataTag {
A = 'A',
B = 'B',
C = 'C',
D = 'D',
}
type Data =
| { tag: DataTag.A; name: string }
| { tag: DataTag.B; value: number }
| DataTag.C
| DataTag.D;
Is the aim to generate a .ts file - or to generate .js files with an associated .d.ts? The latter is definitely more convenient to work with - but TS definitely makes it more difficult here.
Maybe with the .d.ts route, it would be okay to lie for now, raise an issue with TS, and hope this project has enough clout for Microsoft to consider the use-case.
Note this is not a proposal to change the encoding, it is something that works as is today.
The key is that such transformation is mechanical and no copying needed, that is you don’t need a conversion function. (gentype still requires an conversion under the hood), this is useful when you own the data.
Would we ever consider changing the encoding? The current encoding makes variants more difficult to consume in TypeScript than they would be if all variants were objects with a tag property.
Current
if (typeof data === "object") {
switch (data.tag) {
case DataTag.A:
// ...
break;
case DataTag.B:
// ...
break;
}
} else {
switch (data) {
case DataTag.C:
// ...
break;
case DataTag.D:
// ...
break;
}
}
Proposed
switch (data.tag) {
case DataTag.A:
// ...
break;
case DataTag.B:
// ...
break;
case DataTag.C:
// ...
break;
case DataTag.D:
// ...
break;
}
If there are perf concerns we could make this the proposed behaviour opt-in with decorator on the type.