如何防止Flowtype统一类型?

时间:2017-02-15 16:24:00

标签: javascript flowtype

我希望有人可以帮助识别我在下面的类型中遗漏的内容。我创建了一个名为match的函数,它接受一个元组数组 在识别时,将执行匹配功能。例如:

type Fn1<A, B> = (a: A, ...rest: empty[]) => B;
declare export function match<A>(
  xs: Array<[Class<A>, (x: A) => mixed]>,
  ...rest: empty[]
): Fn1<A, mixed>;

describe('match', () => {
    class FileExists {}
    class FileDoesNotExist {}
    let matcher: (x: FileExists | FileDoesNotExist) => mixed;

    beforeEach(() => {
      const whenFileExists = [FileExists, (x: FileExists) => x];
      const whenFileDoesNotExist = [FileDoesNotExist, (x: FileDoesNotExist) => x];
      matcher = match([whenFileExists, whenFileDoesNotExist]);
    });

    it('should return an instance of whenFileDoesNotExist', () => {
      // Should invoke the function in the tuple containing the FileDoesNotExist class
      expect(matcher(new FileDoesNotExist()) instanceof FileDoesNotExist).toBe(true);
    });
});

问题似乎是这些类型正在统一。我收到以下错误:

1406:     let matcher: (x: FileExists | FileDoesNotExist) => mixed;
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FileDoesNotExist. This type is incompatible with the expected param type of
1409:       const whenFileExists = [FileExists, (x: FileExists) => x];
                                                    ^^^^^^^^^^ FileExists

test/fp.test.js:1406
1406:     let matcher: (x: FileExists | FileDoesNotExist) => mixed;
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FileExists. This type is incompatible with the expected param type of
1412:         (x: FileDoesNotExist) => x
                  ^^^^^^^^^^^^^^^^ FileDoesNotExist

test/fp.test.js:1409
1409:       const whenFileExists = [FileExists, (x: FileExists) => x];
                                    ^^^^^^^^^^ FileExists. This type is incompatible with the expected param type of
1412:         (x: FileDoesNotExist) => x
                  ^^^^^^^^^^^^^^^^ FileDoesNotExist

test/fp.test.js:1409
1409:       const whenFileExists = [FileExists, (x: FileExists) => x];
                                                                   ^ FileExists. This type is incompatible with the expected param type of
1412:         (x: FileDoesNotExist) => x
                  ^^^^^^^^^^^^^^^^ FileDoesNotExist

test/fp.test.js:1411
1411:         FileDoesNotExist,
              ^^^^^^^^^^^^^^^^ FileDoesNotExist. This type is incompatible with the expected param type of
1409:       const whenFileExists = [FileExists, (x: FileExists) => x];
                                                    ^^^^^^^^^^ FileExists

test/fp.test.js:1412
1412:         (x: FileDoesNotExist) => x
                                       ^ FileDoesNotExist. This type is incompatible with the expected param type of
1409:       const whenFileExists = [FileExists, (x: FileExists) => x];
                                                    ^^^^^^^^^^ FileExists

我无法弄清楚我在这里缺少什么。有没有人看到我遗漏或遗忘的东西?

2 个答案:

答案 0 :(得分:0)

类型变量func createDate(stringDate: String) -> Date { let formatter = DateFormatter() formatter.dateFormat = "yyyy.MM.dd" return formatter.date(from: stringDate)! } func sumAmounts(_ data: [Item]) -> [Item] { var dict = [Double: Double]() data.forEach { item in let key = item.date.timeIntervalSince1970 var amount = dict[key] ?? 0 amount += item.amount dict[key] = amount } var newData = [Item]() for (key, val) in dict { newData.append(Item(date: Date.init(timeIntervalSince1970: key), amount: val)) } return newData } 必须在A内使用的任何位置解析为相同的类型,这会导致问题。

如果删除参数并尝试执行Flow尝试执行的操作,则会遇到同样的问题。您希望类型match是最常见的情况(我称之为A):

Existence

但这不起作用,因为type Existence = FileExists | FileDoesNotExist declare export function match( xs: Array<[Class<Existence>, (x: Existence) => mixed]>, ...rest: empty[] ): Fn1<Existence, mixed>; 不是一个类,所以Existence没有任何意义。

您需要Class<Existence>中的A对于数组的每个元素都不同,这是不可能的。相反,您可以使用通配符类型(有时称为存在量化类型),由Class<A>表示:

*

此处,declare export function match<A>( xs: Array<[Class<*>, (x: A) => mixed]>, ...rest: empty[] ): Fn1<*, mixed>; 将被解析为A,但在使用FileExists | FileDoesNotExist的每个地方,可以推断为*或{{1}根据用法。

除了在this blogpost之外,我没有记录在任何地方,所以我对它的语义并不是100%肯定。可能需要花一点时间才能完全正确使用。

答案 1 :(得分:0)

存在主义类型是个好主意,但它的问题在于它在应该的时候没有发现错误。例如,如果我执行以下操作:

declare export function match<A>(
  xs: Array<[Class<*>, (x: A) => mixed]>,
  ...rest: empty[]
): Fn1<*, mixed>;

const whenFileExists = [FileExists, (x: FileExists) => x];
const whenFileDoesNotExist = [FileDoesNotExist, (x: string) => x];
matcher = fp.match([whenFileExists, whenFileDoesNotExist]);
(matcher(new FileDoesNotExist()): mixed);

我们应该在将x指定为string时收到错误,因为它应该是FileDoesNotExist的实例。但是,流程不会捕获此错误。事实证明,有一种方法可以以为每个案例添加声明为代价来完成这项工作。这意味着有两个条目的情况下的声明,以及有三个条目时的单独声明。需要为n条目做出声明:

declare export function match<A, B>(
  xs: [[Class<A>, (x: A) => mixed], [Class<B>, (x: B) => mixed]],
  ...rest: empty[]
): (x: A | B) => mixed;

declare export function match<A, B, C>(
  xs: [[Class<A>, (x: A) => mixed], [Class<B>, (x: B) => mixed], [Class<C>, (x: C) => mixed]],
  ...rest: empty[]
): (x: A | B | C) => mixed;

虽然为每个案例写出一份声明是很痛苦的,但在大多数情况下,不太可能有超过5个项目要匹配。我应该能够安全地添加最多5个条目的声明。转到上面的代码片段,这将捕获x作为字符串并抛出以下错误:

This type is incompatible with the expected param type of string: test.js:xx

如果流程能够使用存在类型推断出这将是很好的。