Tagged union types

TypeScript 2.0 implements support for tagged (or discriminated) union types.Specifically, the TS compiler now support type guards that narrow union types based on tests of a discriminant property and furthermore extend that capability to switch statements.

Example

  1. interface Square {
  2. kind: "square";
  3. size: number;
  4. }
  5. interface Rectangle {
  6. kind: "rectangle";
  7. width: number;
  8. height: number;
  9. }
  10. interface Circle {
  11. kind: "circle";
  12. radius: number;
  13. }
  14. type Shape = Square | Rectangle | Circle;
  15. function area(s: Shape) {
  16. // In the following switch statement, the type of s is narrowed in each case clause
  17. // according to the value of the discriminant property, thus allowing the other properties
  18. // of that variant to be accessed without a type assertion.
  19. switch (s.kind) {
  20. case "square": return s.size * s.size;
  21. case "rectangle": return s.width * s.height;
  22. case "circle": return Math.PI * s.radius * s.radius;
  23. }
  24. }
  25. function test1(s: Shape) {
  26. if (s.kind === "square") {
  27. s; // Square
  28. }
  29. else {
  30. s; // Rectangle | Circle
  31. }
  32. }
  33. function test2(s: Shape) {
  34. if (s.kind === "square" || s.kind === "rectangle") {
  35. return;
  36. }
  37. s; // Circle
  38. }

A discriminant property type guard is an expression of the form x.p == v, x.p === v, x.p != v, or x.p !== v, where p and v are a property and an expression of a string literal type or a union of string literal types.The discriminant property type guard narrows the type of x to those constituent types of x that have a discriminant property p with one of the possible values of v.

Note that we currently only support discriminant properties of string literal types.We intend to later add support for boolean and numeric literal types.