代码之家  ›  专栏  ›  技术社区  ›  sdgfsdh

使用流类型系统强制执行运行时检查?

  •  0
  • sdgfsdh  · 技术社区  · 6 年前

    在我的应用程序中,UUID只是一个符合特定regex的字符串:

    '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
    

    但是,我希望将UUID作为字符串的现有代码继续工作。

    这在流动中是可能的吗?

    // @flow
    
    type Uuid = string;
    
    const ensureUuid = (x : string) : Uuid => {
      if (!isUuid(x)) {
        throw new TypeError(x + ' is not a Uuid');
      }
    
      return x;
    };
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   sdgfsdh    6 年前

    这可以使用 opaque 关键词:

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

    Docs: 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);
    

    使用此设置,a Uuid 只能通过以下方式创建实例 uuid.create .