16

Worded an other way:

How would you type the windowState DOM property in TypeScript?

SOLVED (in TypeScript 2):

declare var windowState: WindowState const enum WindowState { STATE_MAXIMIZED = 1, STATE_MINIMIZED = 2, STATE_NORMAL = 3, STATE_FULLSCREEN = 4 } ... var windowState = 5 // Type Error, as expected! 

Original question:

How do I declare a type in TypeScript so that it describes an algebraic data type? The purpose of this is describing an existing API.

When I try the following, TypeScript obviously complains that a type is expected:

type Weather = 'sunny' | 'bad' 

One idea I had is using a JavaScript 2015 Symbol, however TypeScript doesn't seem to know about these.

An other idea was using an enum, however TypeScript complains that a member initializer must be constant expression:

const enum Weather { sunny = 'sunny', bad = 'bad', windy = Symbol('windy') } 

I would have thought that a string constant is a constant expression.

11
  • For this particular example, you could probably use an enum (need to convert to/from String in places, though) Commented Nov 25, 2015 at 11:38
  • Yes, I'll add that to the question as well, as an enum was not really satisfactory Commented Nov 25, 2015 at 11:39
  • TypeScript does understand Symbols. If your runtime environment supports it, you can either load the lib.es6.d.ts or you can replicate the Symbol interface. Commented Nov 25, 2015 at 11:46
  • @Kitson Does it regard them as a constant expression, though? Commented Nov 25, 2015 at 11:47
  • 1
    TypeScript 1.8 will introduce "String Literal Types": github.com/Microsoft/TypeScript/pull/5185 Commented Dec 7, 2015 at 14:05

2 Answers 2

36

TypeScript 2.0 has support for discriminated unions/algebraic data types. The documentation is here.

You can combine string literal types, union types, type guards, and type aliases to build an advanced pattern called discriminated unions, also known as tagged unions or algebraic data types. Discriminated unions are useful in functional programming. Some languages automatically discriminate unions for you; TypeScript instead builds on JavaScript patterns as they exist today. There are three ingredients:

  1. Types that have a common literal (or enum) property — the discriminant.
  2. A type alias that takes the union of those types — the union.
  3. Type guards on the common property.

Let's start:

interface Square { kind: "square"; size: number; } interface Rectangle { kind: "rectangle"; width: number; height: number; } interface Circle { kind: "circle"; radius: number; } 

First we declare the interfaces we will union. Each interface has a kind property with a different string literal type. The kind property is called the discriminant or tag. The other properties are specific to each interface. Notice that the interfaces are currently unrelated. Let's put them into a union:

type Shape = Square | Rectangle | Circle; 

Now let's use the discriminated union:

function area(s: Shape) { switch (s.kind) { case "square": return s.size * s.size; case "rectangle": return s.height * s.width; case "circle": return Math.PI * s.radius ** 2; } } 

In each of those branches, TypeScript will narrow down the type. If you try to use a case clause with a value that isn't present as any kind property, then TypeScript will error.

Sign up to request clarification or add additional context in comments.

1 Comment

Can you give an example how to call such a function with a literal argument of that type? I guess I'm confused by false analogies to the Haskell syntax.
5

To use enum's you would write it something like this:

enum Weather { Sunny, Windy }; let currently = Weather.Sunny; 

String Literals

The other way the OP is asking for this to be solved are arriving in TypeScript 1.8.

They would allow you to do something like this:

type Weather = 'Sunny' | 'Windy'; 

4 Comments

But I need a type that can describe being one of several string constants.
why do you need string constants?
There is an existing API and this would be a typing for it,
What you are asking for is coming in TypeScript 1.8 (which is currently available for testing via npm install typescript@next

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.