Flow 中对包含可选参数的联合类型进行操作的注释函数

Annotating functions in Flow that operate on union types containing optional parameters

我为糟糕的标题道歉,我不知道如何更好地描述它:)

我在基于具有 auto-incrementing ID 的 IndexedDB 数据库的应用程序中使用 Flow。所以基本上,我创建了一些 objects(没有 id 属性),将它们写入数据库(此时它们被赋予 id 属性通过 IndexedDB),然后将它们读回(从数据库读取的任何 object 保证有一个数字 id 属性)。

我有一些函数可以在这些 object 上运行。有时它们只对有 ID 的 object 进行操作,有时它们只对没有 ID 的 object 进行操作,有时它们对两者都进行操作。最棘手的是后一种情况。这是一种尝试,在写入数据库之前和之后对 object 使用两种不同的类型(因此,分别没有和使用 id 属性):

/* @flow */

type BeforeDb = {prop: string};
type AfterDb = BeforeDb & {id: number};

var beforeDb: BeforeDb = {prop: 'hi'};
var afterDb: AfterDb  = {id: 1, prop: 'hi'};

function a(obj: BeforeDb | AfterDb): BeforeDb | AfterDb {
  if (typeof obj.id === 'number') {
    console.log(obj.id * 2);
  }
  return obj;
}

function b(obj: AfterDb) {}

var x = a(afterDb);
b(x);

(demo link)

这会在最后一行产生错误,因为它不知道 xAfterDb 类型,而且我不确定如何适当地传达该信息。

另一个想法是使用 bounded polymorphisms,但我不相信这可以创建类似我上面的 a 函数的东西,因为它无法处理 id有时是未定义的。就像我想做这样的事情:

function a<T: {id?: number}>(obj: T): T {
  if (typeof obj.id === 'number') {
    console.log(obj.id * 2);
  }
  return obj;
}

(demo link)

但这不起作用。如果我为 id 分配了一个虚拟值,所以它始终是数字(比如 -1 而不是未定义)那么这会起作用,但是我必须非常小心记住删除 id在第一次写入数据库之前,所以真正的 id 可能是 auto-generated,这会很丑陋。

在那之后,我几乎没有什么好主意了。我要做的一件事就是只使用一种类型,比如:

type Obj = {id?: number, prop: string};

然后明确检查 id 属性 是否存在于每个使用 id 属性 的函数中。但这很烦人,因为我有一堆函数只用 IndexedDB 的输出调用,所以我已经知道 id 肯定在那里。我只是不知道如何告诉 Flow。

有什么想法吗?

function a<T: BeforeDb | AfterDb>(obj: T): T {
  if (typeof obj.id === 'number') {
    console.log(obj.id * 2);
  }
  return obj;
}