Algebraiske datatyper i TypeScript


Formulert på en annen måte:

Hvordan ville du skrevet windowState-DOM-egenskapen i TypeScript?

LØST (i 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!

Originalspørsmål:

Hvordan kan jeg deklarere en type i TypeScript slik at den beskriver en algebraisk datatyper? Hensikten med dette er å beskrive et eksisterende API.

Når jeg prøver følgende, klager TypeScript åpenbart over at en type forventes:

type Weather = 'sunny' | 'bad'

En idé jeg hadde er å bruke en JavaScript 2015 Symbol, men det virker som TypeScript ikke kjenner til disse.

En annen idé var å bruke en enum, men TypeScript klager på at en medlemsinitialisator må være et konstant uttrykk:

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

Jeg ville ha trodd at en string-konstant er et konstant uttrykk.

Svar #1

TypeScript 2.0 har støtte for diskriminerte unions/algbriske datatyper. Dokumentasjonen er her.

Du kan kombinere strengliteraltyper, unionstyper, typevaktsmekanismer og typealiaser for å bygge et avansert mønster kalt diskriminert union, også kjent som merket union eller algebraiske datatyper. Diskriminerte unioner er nyttige innen funksjonell programmering. Noen språk diskriminerer unioner automatisk for deg; TypeScript bygger derimot på JavaScript-mønstre slik de eksisterer i dag. Det er tre ingredienser:

  1. Typer som har en felles litteral (eller enum) egenskap — diskriminanten.
  2. Et typealias som tar unionen av disse typene — unionen.
  3. Typebeskyttere på den vanlige egenskapen.

La oss begynne:

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

Først erklærer vi grensesnittene vi vil slå sammen. Hvert grensesnitt har en kind-egenskap med en forskjellig strengliteraltype. kind-egenskapen kalles diskriminanten eller taggen. De andre egenskapene er spesifikke for hvert grensesnitt. Legg merke til at grensesnittene foreløpig er urelaterte. La oss sette dem sammen i en union:

type Shape = Square | Rectangle | Circle;

La oss nå bruke den diskriminerte unionen:

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;
    }
}

I hver av disse grenene vil TypeScript innsnevre typen. Hvis du prøver å bruke en case-setning med en verdi som ikke er til stede som noen kind-egenskap, vil TypeScript gi en feil.

Svar #2

For å bruke enums ville du skrevet det omtrent slik:

enum Weather {
    Sunny,
    Windy
};

let currently = Weather.Sunny;

Strenglitteraler

Den andre måten OP ber om at dette skal løses på kommer i TypeScript 1.8.

De ville la deg gjøre noe slik som dette:

type Weather = 'Sunny' | 'Windy';

Relaterte artikler