2017-02-23 38 views
2

我想定義一個實體映射,使得鍵必須始終是某個指定類型的其他對象的ID。期望使用會是這樣的:使用FlowType鍵入特定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都是相同的底層類型(例如,stringnumber),所以可能會意外編寫類似上面的badMap示例的代碼。我希望FlowType可以幫助防止這種情況,只要我以編程方式構造我的地圖時,我只會引用正確的ID類型。

回答

1

從概念上講,我認爲你想要一個標記爲單個案例的聯合類型來表示你正在使用的各種ID--下面是F#中的一個例子:Single Case Discriminated Unions

如果我的理解正確,您希望創建地圖Map<FooId, Foo>,其中fooMap[someFooId]只能包含Foo對象。但由於FooId可能與BarId類型相同,因此地圖中可能有someBarId: Foo。您希望Flow對可能發生的情況進行類型檢查。

不幸的是,我不相信現在有一種簡單的方法可以在Flow中執行此操作,因爲它缺少單個標記的工會。在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