As a mostly frontend dev I feel it’s worth mentioning that while it’s a nice way to try Gleam, the Gleam aspect of this is inessential, TypeScript is quite good, and using create-tauri-app with the react-ts, vue-ts, or svelte-ts preset is also a great way to try making cross-platform GUIs with Tauri. It uses Vite under the hood, and the generated app is extremely minimal — nothing like the awful create-react-app behemoths that probably left a bad taste in everyone’s mouth.
TypeScript can be quite productive but I find its type system lacking. Specifically the use of structural typing/lack of nominal typing and lack of sum types. I find these extremely useful when writing reliable software, which is why Gleam appeals to me.
Typescript's type system isn't sound. Gleam's is.
Edit: fixed now (might take a few mins for the cache to expire).
Edit: lots of replies showing how TypeScript can be made to do exhaustiveness checking. It's neat and all but it's a lot of gymnastics compared to languages that just have this built in, which again is part of the appeal of Gleam for me.
type Variant = { kind: "value", value: string } | { kind: "error", error: string } | { kind: "unexpected" };
class Unreachable extends Error {
constructor(unexpected: never) {
super(`${unexpected}`);
}
}
function useVariant(variant: Variant) {
switch (variant.kind) {
case "value":
return variant.value;
case "error":
return variant.error;
default:
throw new Unreachable(variant);
}
}
The `new Unreachable(variant)` will fail the type check only when you have not exhaustively matched all variants. type A = { kind: "kindA", a: "dataA" }
type B = { kind: "kindB", b: "dataB" }
type Sum = A | B
const match = <
const V extends { kind: string },
const C extends { [ kind in V[ "kind" ] ]: ( value: V & { kind: kind } ) => unknown }
>( value: V, cases: C ) => cases[ value.kind as V[ "kind" ] ]( value ) as ReturnType<C[ V[ "kind" ] ]>
// You check the type of result, change the type of value to A or B, make the cases non-exhaustive...
const howToUse = ( value: Sum ) => {
const result = match( value, {
kindA: _ => _.a,
kindB: _ => _.b
} )
}
You can test it here: https://www.typescriptlang.org/play?#code/C4TwDgpgBAglC8UDeU...EDIT: and an example of usage: https://github.com/wwtos/mjuo/blob/ca8c514185c1b5bb22aec752a...
function expectType<A, B extends A>() {}
expectType<never, typeof yourUnion>();
The function call will fail at compile time if yourUnion is anything more than never, which you can use in your else case of if statements that narrow the discriminated union.