2017-02-23 3 views
2

Ich möchte eine Entity Map so definieren, dass die Schlüssel immer IDs eines anderen Objekts eines bestimmten Typs sein müssen. Die gewünschte Nutzung wäre so etwas wie:Typspezifische IDs mit FlowType

export type Id<T> = { 
    id: string 
}; 

export type Foo = Id<Foo> & { 
    val: number 
}; 

// type Foo is now {id: Id<Foo>, val: number} 

export type Bar = Id<Bar> & { 
    val: number 
}; 

// type Bar is now {id: Id<Bar>, val: number} 

// FooMap should only be able to store objects of type foo, referenced by 
// IDs of type Id<Foo> 
export type FooMap = { 
    [key: Id<Foo>]: Foo 
}; 

const foo1: Foo = { id: "foo1", val: 1 }; 
const foo2: Foo = { id: "foo2", val: 2 }; 
const bar1: Bar = { id: "bar1", val: 3 }; 

// This would pass type checking: 
const fooMap: FooMap = { 
    [foo1.id]: foo1, 
    [foo2.id]: foo2 
}; 

// But this would fail type checking: 
const badMap: FooMap = { 
    [bar1.id]: foo1 
}; 

Mein Anwendungsfall ist, dass ich eine normierte Objektstruktur haben will, die Beziehungen über IDs verwaltet. Aber da die IDs alle den gleichen zugrunde liegenden Typ aufweisen (z. B. string oder number), wäre es möglich, versehentlich etwas wie das obige Beispiel zu codieren. Ich hoffe, dass FlowType dazu beitragen kann, dies zu verhindern, indem ich immer nur den korrekten ID-Typ referenziere, wenn ich meine Maps programmatisch erstelle.

Antwort

1

Konzeptionell denke ich, dass Sie einen einzelnen Fall Tag Union Typ die verschiedenen IDs, die Sie verwenden möchten - hier ein Beispiel für das, was in F # ist: Single Case Discriminated Unions. Wenn ich Sie richtig verstehe, möchten Sie Karten wie Map<FooId, Foo> erstellen, wobei fooMap[someFooId] nur ein Foo Objekt enthalten kann. Aber da FooId möglicherweise der gleiche Typ wie BarId ist, können Sie möglicherweise someBarId: Foo in der Karte haben. Sie möchten Flow zu typecheck Fällen, wo das passieren könnte.

Leider glaube ich nicht, dass es einen einfachen Weg gibt, dies jetzt in Flow zu tun, da es keine single-case-markierten Verbindungen gibt. In F # könnten Sie so etwas tun:

type FooId = FooId of string 
type BarId = BarId of string 

type Id<'T> = Id of 'T // you'd pass in FooId as the type 

type Foo = { 
    id: Id<FooId>; 
    value: string; 
} 

type Bar = { 
    id: Id<BarId>; 
    value: string; 
} 

let foo: Foo = { id = Id (FooId "12345"); value = "fooVal" } 
let bar: Bar = { id = Id (BarId "12345"); value = "barVal" } 
let fooMap = 
    Map.empty<Id<FooId>, Foo> 
    |> Map.add (Id (BarId "12345")) foo 

Das nächste, was ich bekommen konnte, war etwas in JavaScript. Das Problem dabei ist, dass der Typfehler bei der Definition von FooId/BarId auftritt. Wir erhalten:

string literal `FooId`. Expected string literal `BarId`, got `FooId` instead 
string literal `BarId`. Expected string literal `FooId`, got `BarId` instead 

Das JavaScript:

type FooId = "FooId"; 
type BarId = "BarId"; 
type Id<T> = { 
    id: string; 
    type: T 
}; 

type Foo = { 
    id: Id<FooId>, 
    val: any 
}; 

type Bar = { 
    id: Id<BarId>, // get type error here 
    val: any 
}; 

type FooMap = { 
    [key: Id<FooId>]: Foo // get type error here 
}; 

const fooIdBuilder = (id): Id<FooId> => ({ id, type: "FooId" }); 
const barIdBuilder = (id): Id<BarId> => ({ id, type: "BarId" }); 

const foo1: Foo = { 
    id: fooIdBuilder("12345"), 
    val: "fooval" 
}; 

const foo2: Foo = { 
    id: fooIdBuilder("23456"), 
    val: "fooval" 
}; 

const bar1: Bar = { 
    id: barIdBuilder("23456"), 
    val: "barval" 
}; 

const fooMap: FooMap = {}; 
fooMap[foo1.id] = foo1; 
fooMap[bar1.id] = foo2; 

Sie können diese kopieren und in Try Flow

+0

Yep, Ihre Antwort ist einfach, wo ich in meinem Denken endete. Ich stimme zu, dass mein ursprünglicher Anwendungsfall derzeit in Flow nicht möglich scheint, und stimme weiterhin zu, dass der Fehlerbericht auf der Deklarationsseite und nicht auf der Website irreführend ist. :) Danke, dass Sie sich die Zeit genommen haben, den Beispielcode zusammenzustellen! – Palpatim