2017-02-23 14 views
2

キーが常に指定されたタイプの他のオブジェクトのIDでなければならないようにエンティティマップを定義したいと思います。希望利用がのようになります。フロータイプの特定のIDを入力してください

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

私のユースケースは、私はIDを経由して関係を管理正規化されたオブジェクト構造を持つようにしたいということです。しかしIDはすべて同じ基底型(例:stringまたはnumber)であるため、上記の例のようなコードを誤ってコード化する可能性があります。私は、FlowTypeが、プログラムでマップを構築しているときに正しいIDの型だけを参照することを強制することによって、それを防ぐのに役立つことを願っています。

答えて

1

概念的には、あなたが使用しているさまざまなIDを表現するために単一のケースタグ付きの共用体タイプを望むと思います。ここにF#の例があります:Single Case Discriminated Unions

あなたが正しく理解している場合、Map<FooId, Foo>のようなマップを作成したいと考えています。fooMap[someFooId]にはFooオブジェクトしか含まれていません。しかしFooIdBarIdと同じタイプかもしれないので、潜在的にsomeBarId: Fooをマップに持つことができます。あなたは、フローが起こる可能性があるケースをタイプチェックするようにします。

残念ながら、これはFlowで直ちに行うことは簡単ではないと思います。なぜなら、1つのケースのタグ付きユニオンがないからです。 F#では次のようなことができます:

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 

私が得ることができる最も近いものはJavaScriptのようなものでした。これの問題は、型エラーがFooId/BarIdの定義で発生することです。我々が得る:

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

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

あなたは、あなたの答えはちょうど私が私の考えで終わった場所についてである、Try Flow

+0

うんにこれをコピーして貼り付けることができます。私は、Flowでオリジナルのユースケースが現在可能ではないように思っています。また、サイトを使用するのではなく、宣言サイトのエラーレポートが誤解を招くことに同意します。 :)サンプルコードをまとめる時間をとってくれてありがとう! – Palpatim

関連する問題