re: Tag Your Unions Before You Wreck Your Unions VIEW POST


TypeScript has untagged sum types, but it's kind of a pain to handle the cases because you have to write the runtime checks manually:

type Get = {queryParams: string};
type Put = {body: string};
type HttpMethod = Get | Put;

namespace HttpMethod {
  export function isGet(httpMethod: HttpMethod): httpMethod is Get {
    return (<Get>httpMethod).queryParams !== undefined;

type Req = {path: string, httpMethod: HttpMethod};

function handleRequest(req: Req): void {
  if (HttpMethod.isGet(req.httpMethod))
    console.log(`GET ${req.path} params: ${req.httpMethod.queryParams}`);
    console.log(`PUT ${req.path} body: ${req.httpMethod.body}`);

Oh, and the checks would be different if the cases were primitive types or function-constructed values.

IMO tagged unions are much quicker to handle precisely because the tags (i.e. data constructors) are first-class language entities.

You can "simplify" the type checking part by making the method part of your type definition and setting it's type to the string literal:

type Get = {
  method: 'GET';

Inside of an if/switch-case that checks method typescript only allows access to the proper fields.
In case you cover all cases in a switch, inside default, the type of the variable will be never.
Typescript calls this "Discriminated Unions":
(There is no way to link directly to the section, you need to search for the term on the page.)

code of conduct - report abuse