[英]Flowtype: generic Id<T> type with similar constraints to the type argument passed in
我有一個通用的Id<T: HasId>
類型,在結構上始終只是一個string
而與作為T
傳入的類型參數無關。 我希望將具有不同類型的Id<T>
類型作為T
傳遞,以表現出不同的類型。
例如,我希望以下代碼中的片段const i :Id<Car> = p.id
導致Flow錯誤:
declare interface HasId {
id: string,
};
type Id<T: HasId> = string;
type Person = {
id: Id<Person>,
name: string,
};
type Car = {
id: Id<Car>,
make: string,
model: string,
};
const p :Person = { id: '1234', name: 'me' }
const c :Car = p; // Causes a Flow error, good!
const c :Id<Car> = p.id; // I want this to cause a Flow error,
// but currently it doesn't.
此外,如果這可以繼續與聯合類型一起很好地工作,那就太好了:
type Vehicle =
| Car
| Motorcycle
;
const t :Car = { id: '5678', make: 'Toyota', model: 'Prius' };
const v :Id<Vehicle> = c.id; // Currently does not cause Flow
// error; I want to keep it that way.
聽起來您想要的是不透明的類型,而Flow還沒有。 如果您具有類型別名type MyString = string
,則可以將string
和MyString
互換使用。 但是,如果您具有不透明類型別名opaquetype MyNumber = number
,則不能互換使用number
和MyNumber
。
有關此GitHub問題的不透明類型的詳細說明。
我做了一些實驗,發現了一種方法,可以根據GitHub問題注釋中顯示的系統及其后的內容來完成我在問題中指定的操作。 您可以使用Flow處理不透明類型的類(具有通用類型參數T
),並使用強制轉換為any
類型,以便在字符串和ID之間進行轉換。
這是一些啟用此功能的實用程序:
// @flow
import { v4 } from 'node-uuid';
// Performs a "type-cast" from string to Id<T> as far as Flow is concerned,
// but this is a no-op function
export function stringToId<T>(s :string):Id<T> {
return (s :any);
}
// Use this when you want to treat the ID as a string without a Flow error
export function idToString(i :Id<*>):string {
return (i :any);
}
export function createId<T>():Id<T> {
return stringToId('1234');
}
// Even though all IDs are strings, this type distinguishes between IDs that
// can point to different objects.
export class Id<T> {};
使用這些實用程序,以下代碼(類似於我的問題中的原始代碼)將導致Flow錯誤,就像我想要的那樣。
// @flow
const p :Id<Person> = createId<Person>();
// note: Even though p is Id<Person> in Flow, its actual runtime type is string.
const c :Id<Car> = p; // this causes Flow errors. Yay!
// Also works without an explicit annotation for `p`:
const pp = createId<Person>();
const cc :Id<Car> = pp; // also causes Flow errors. Yay!
不幸的是,由於類似的類型錯誤會觸發多個Flow錯誤,因此Flow輸出非常冗長。 即使輸出不是理想的,但至少它的行為正確,因為出錯會導致Flow報告錯誤。
另一個問題是,使用此解決方案時,在Flow不期望Id<*>
對象/映射鍵之類的情況下,您必須從ID顯式轉換為字符串,例如以下示例:
// @flow
type People = { [key :string]: Person };
const people :People = {};
const p :Id<Person> = createId<Person>();
people[p] = createPerson(); // causes Flow error
// Unfortunately you always have to do this:
people[idToString(p)] = createPerson(); // no Flow error
這些類型轉換函數在運行時只是禁止操作,因為所有的Flow類型都被剝離了,因此,如果您多次調用它們,可能會降低性能。 有關更多討論,請參見我在此答案中鏈接的GitHub問題。
注意:我正在使用Flow v0.30.0。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.