在Flow中对内置类型进行子分类

Subtyping built-in types in Flow

本文关键字:分类 置类型 内置 Flow      更新时间:2023-09-26

假设我正在编写处理uuid的代码。在内部,我想将它们表示为字符串。也就是说,每个UUID都是字符串,但不是每个字符串都是有效的UUID,而且我不希望意外地将错误的东西赋给用于保存UUID的变量。所以我想创建一个类型'uuid',这样赋值就会失败:

let foo: uuid = "Some string"

但这应该成功:

function create_uuid(): uuid; { /* implementation? */ }
let foo: uuid = create_uuid(); 
let bar: string = uuid;  // this is fine

是否有任何方法可以创建具有这些属性的流类型?我在我的研究中发现了$Subtype,并认为这可能有效:

type uuid = $Subtype<string>;

但是由于某些原因,它仍然允许从字符串中赋值

有以下hack(缺点是UUID也将是Object):

// keep this constructor private
class IsUUID {}
export type UUID = string & IsUUID;
export function create(): UUID {
  const uuid = 'blah' // <= your implementation
  return ((uuid: any): UUID)
}
// tests
declare function f(uuid: UUID): void;
declare function g(s: string): void;
declare function h(o: Object): void;
let foo = create()
let bar: string = foo // <= ok
f(foo) // <= ok
f(bar) // <= error: string. This type is incompatible with IsUUID
g(foo) // <= ok
g(bar) // <= ok
h(foo) // <= ok :(

编辑:这个答案已经过时了。自从提出这个问题以来,Flow已经实现了不透明类型。参考ESRogs的回答

可能有一些hack可以解决这个问题,但是你要求的是一个不透明的数据类型,Flow目前不支持它们。以下是GitHub上Flow存储库对它们的一些讨论。

使用带有子类型约束的不透明类型。来自文档:

exports.js

export opaque type ID: string = string;

imports.js

import type {ID} from './exports';
function formatID(x: ID): string {
    return "ID: " + x; // Ok! IDs are strings.
}
function toID(x: string): ID {
    return x; // Error: strings are not IDs.
}
https://flow.org/en/docs/types/opaque-types/