簡體   English   中英

流類型:通用ID <T> 具有與傳入的類型參數相似的約束的類型

[英]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 ,則可以將stringMyString互換使用。 但是,如果您具有不透明類型別名opaquetype MyNumber = number ,則不能互換使用numberMyNumber

有關此GitHub問題的不透明類型的詳細說明。

不錯,但有點hacky的解決方案

我做了一些實驗,發現了一種方法,可以根據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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM