3 ms·
Right, TS wants you to go full-on tagged union. {type: 'a', a: number} | {type: 'b', b: string} When you do this you consume it with a switch(x.type) stat
by crdrost 7y ago
Right, TS wants you to go full-on tagged union.
{type: 'a', a: number} | {type: 'b', b: string}
When you do this you consume it with a switch(x.type) statement, not an if() statement, so that you can write `default: assertImpossible(x.type)` for some function `assertImpossible(y: never): never` that throws an exception of your choice.
This gives clear fail-at-runtime semantics if JS hands you some crap your types weren’t anticipating, but also fail-at-compile-time semantics if your pattern match is no longer exhaustive (i.e. you added a new case, now TypeScript tells you everything you need to change).
In general it's really nice to view any data structure as isomorphic to the control structure that consumes it. “lists” are consumed by for..of loops, “dicts” are consumed by for..of Object.keys() loops, sum types are consumed by switches, objects are consumed by property accesses, etc.
- danenania 7y agoRight, tagged unions are what I use, but when you start having many variants, nested tagged unions, etc., it gets quite awkward imo when you need a big ol’ switch statement to get at some property or other instead of just a simple property check.
- bcherny 7y agoIf your code is fully typed, you don't need the default, since you know at compile time that you've handled your cases (Playground link: [0]): type Props = | { type: 'a', a: number } | { type: 'b', b: string } function f(props: Props): number { switch (props.type) { case 'a': return 1 case 'b': return 2 } } Unfortunately you're right that this breaks when using `if` instead of `switch` (unless you have a default return at the end of your function, or replace one of your `if`s with an `else`; Playground link: [1]): type Props = | { type: 'a', a: number } | { type: 'b', b: string } // Error TS2366: Function lacks ending return statement and return // type does not include 'undefined'. function f(props: Props): number { if (props.type === 'a') { return 3 } if (props.type === 'b') { return 4 } } This second case is a bug in TSC, and is tracked in https://github.com/microsoft/TypeScript/issues/18319 https://github.com/microsoft/TypeScript/issues/18319. [0] https://www.typescriptlang.org/play/#code/AQ4FwTwBwU2AFATgeygZ2AXgFClAH2AG9xoYAuYAcgEMqAaYGygOwFcBbAIxkWAF9ceYIRKRYlKlwbAulNGEQBLFgHMB2ISABmbFgGMwS5C2DaAFFBTpKSVGgCUrTjz5EtoNAHclYfQAtgS2s0ADpxGAdiDzx9GjQ4WipKRBgwNkRTAGYY0DiE6mkUtIzTABZcwTxBIA https://www.typescriptlang.org/play/#code/AQ4FwTwBwU2AFATgey... [1] https://www.typescriptlang.org/play/#code/AQ4FwTwBwU2AFATgeygZ2AXgFClAH2AG9xoYAuYAcgEMqAaYGygOwFcBbAIxkWAF9ceYIRKRYlKlwbAulNGEQBLFgHMB2ISAD024AFFEKPgBUAygCYAzADYblAGJsWAYzBLkLYABsaLgNYYMCwAJirqiDBgbIheCjRgMBzBYEyhwJHRsVrAuqSwwCHIMBgsyKkqLt5sIXBUzrUAZiowIVQAdDmNzm4eXo0AFFAo6JRIqGgAlKycPHxEOSBKjcBDI2jt4nCYO9R0k8SLoJkxXlZHgsLAy6vDE5tkWLtSVAcLVyAnscAALBc5giAA https://www.typescriptlang.org/play/#code/AQ4FwTwBwU2AFATgey...