使用Flow类型系统执行运行时检查?

时间:2018-08-01 16:01:08

标签: javascript flowtype

在我的应用程序中,UUID只是一个符合特定正则表达式的字符串:

'0b4ba6ba-496f-11e8-a21b-06f9c13aa914' // UUID

'hello world' // Not UUID

现在,我可以像这样在运行时检查格式:

const isUuid = (x : string) : boolean => {
  const pattern = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
  return pattern.test(x);
};

这是一个运行时检查,但是我可以利用类型系统来确保对所有代码路径执行检查。基本上,我想创建一个类似字符串的类型,用于表示已通过isUuid检查的字符串。

type Uuid = ?

let s : string = '0b4ba6ba-496f-11e8-a21b-06f9c13aa914';

let x : Uuid = s; // Type error

let y : Uuid = ensureUuid(s); // Type checked, but may throw at run-time

但是,我希望以UUIDs作为字符串的现有代码能够继续工作。

在Flow中有可能吗?

// @flow

type Uuid = string;

const ensureUuid = (x : string) : Uuid => {
  if (!isUuid(x)) {
    throw new TypeError(x + ' is not a Uuid');
  }

  return x;
};

1 个答案:

答案 0 :(得分:1)

可以使用opaque关键字:

  

不透明类型别名是不允许在定义它们的文件之外访问其基础类型的类型别名。

文档:https://flow.org/en/docs/types/opaque-types/

您将需要两个文件。

第一个uuid.js

// @flow

export opaque type Uuid = string;

export const is = (x : string) : boolean => {
  const pattern = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;

  return pattern.test(x);
};

export const create = (x : string) : Uuid => {
  if (is(x)) {
    return x;
  }

  throw new TypeError('"' + x + '" is not a valid UUID');
};

然后,消费者index.js

// @flow

import * as uuid from './uuid';

import type { Uuid } from './uuid';

const s : string = '0b4ba6ba-496f-11e8-a21b-06f9c13aa914';

// const q : Uuid = s; // Type error!

const r : Uuid = uuid.create(s); // Type checked, but might throw a run-time error

// const h : Uuid = 'not allowed!';
// const i : Uuid = ('not allowed!' : Uuid);

使用此设置,只能通过Uuid创建一个uuid.create实例。