typed-immutable, 不可变和结构化类型化数据

分享于 

15分钟阅读

GitHub

  繁體 雙語
Immutable and structurally typed data
  • 源代码名称:typed-immutable
  • 源代码网址:http://www.github.com/typed-immutable/typed-immutable
  • typed-immutable源代码文档
  • typed-immutable源代码下载
  • Git URL:
    git://www.github.com/typed-immutable/typed-immutable.git
    Git Clone代码到本地:
    git clone http://www.github.com/typed-immutable/typed-immutable
    Subversion代码到本地:
    $ svn co --depth empty http://www.github.com/typed-immutable/typed-immutable
    Checked out revision 1.
    $ cd repo
    $ svn up trunk
    
    类型不可变的NPM versionBuild Status

    ======== =

    库提供的是基于 immutable.js,利用它是不可变的持久性数据,并提供在该平台之上的结构类型 typing。 库不是针对类型化语言( 某些 static 类型检查器( 如 ) 将是) 提供类型安全的,尽管它允许用户定义结构化的类型和后者。 这种工具的便利用例将是一个应用状态建模( 在 MVC系统中),特别是如果状态是集中的。

    API

    在下面的章节中,我们将使用 term"类型"类that可以通过 function call (although use of new still possible) 实例化,并产生immutable持久的持久数据结构,我们称之为"值"像字符串或者数字那样的primitive。

    记录

    记录是带标签的数据结构。 它们为复杂数据提供了一个轻量级的表示。 可以通过调用带有类型结构说明符的Record 函数来定义类型,这是一个提供与它们关联的字段名称和类型的对象:

    var {Record} =require("typed-immutable")varPoint=Record({x:Number, y:Number})

    记录类型可能作为函数调用或者作为类实例化,以在具有预先定义的结构的不可变对象形式中生成值:

    Point({x:0, y:0}) // => {x:0, y:0}newPoint({x:0, y:0}) // => {x:0, y:0}

    记录类型强制执行预定义的结构,如果提供的输入没有 MATCH,则会失败:

    Point() // => TypeError: Invalid value for"x" field://"undefined" is not a numberPoint({x:"1", y:"2"}) // => TypeError: Invalid value for"x" field://"1" is not a number

    为了便于使用,还可以为feilds提供记录类型的默认值:

    varPoint=Record({x:Number(0), y:Number(0)})Point() // => {"x": 0,"y": 0 }Point({x:20}) // => {"x": 20,"y": 0 }Point({x:null}) // => TypeError: Invalid value for"x" field://"null" is not a number

    记录字段可以通过属性访问语法通过 NAME 访问:

    var p1 =Point({x:17})p1.x// => 17p1.y// => 0

    尝试更新字段将失败,错误为:

    p1.x=30// => TypeError: Cannot set on an immutable record.

    你可以通过改变记录值,或者从现有的方法中创建新值,类似于字符串或者数字:

    p1 =Point() // => {x:0, y:0}p1.set("x", 7) // => {x: 7, y:0}p1 // => {x:0, y:0}

    如果一个记录中的字段被定义为默认值,则Removeing将它的值重置为默认值。

    var p1 =Point({x:1, y:25}) // => {x:1, y:25}p1.remove("y") // => {x:1, y:0}

    记录类型的proudce值只有用它们只是忽略的所有它的他字段定义的字段:

    Point({x:30, y:40, z:8}) // => {x:30, y:40}

    尽管显式禁止使用错误设置未声明的字段:

    Point().set("z", 5) // => TypeError: Cannot set unknown field"z" on"Typed.Record({x: Number(0), y: Number(0)})"

    记录值实际上是记录类型/类的实例,但是给定immutablity的值更常见,这就是为什么我们将它们引用为这样的原因:

    var p1 =Point()
    p1 instanceofPoint// truep1.x// => 0p1.y// => 0var p2 =p1.merge({x:23})
    p2 instanceofPoint// truep2.x// => 23p2.y// => 0p1.equals(Point()) // => truep1.equals(p2) // => falsep2.equals(Point({x:23})) // => true

    记录值序列化为包含它的值和类型签名的字符串

    Point({x:23}).toString() // => 'Typed.Record({x: Number(0), y: Number(0)})({"x": 23,"y": 0 })'

    但是对于具有大量字段的记录,提供 NAME 可能更方便,这可以在定义过程中完成:

    varPoint=Record({x:Number(0), y:Number(0)}, "Point")Point({x:4, y:7}).toString() // => 'Point({"x": 4,"y": 7 })'
    嵌套记录

    对于定义记录的任何复杂数据,contaning记录都是关键的,这完全按照预期工作:

    var Line =Record({begin:Point, end:Point}, "Line")var line =Line({end: {x:70}})
    line instanceof Line // => trueline.toString() // => Line({"begin": Point({"x": 0,"y": 0 }),"end": Point({"x": 70,"y": 0 }) })line.begin// => {x: 0, y:0}line.begininstanceofPoint// => trueline.end// => {x: 70, y:0}line.endinstanceofPoint// => true

    原始字段一样,你也可以向复杂记录提供默认值:

    var Line =Record({begin:Point({x:23}), end:Point({y:4})}, "Line")Line().toString() //=> Line({"begin": Point({"x": 23,"y": 0 }),"end": Point({"x": 0,"y": 4 }) })

    记录可以序列化为 JSON,然后实例化为相同的记录值:

    Line(line.toJSON()).equals(line) // => true

    下拉列表

    你可以通过提供 List 函数的类型来定义类型化列表:

    var {List} =require("typed-immutable")var Numbers =List(Number)Numbers().toString() // 'Typed.List(Number)([])'Numbers.of(1, 2, 3).toString() // => 'Typed.List(Number)([ 1, 2, 3 ])'

    类型列表只能包含该类型的项,如果尝试进行其他操作,则该列表将失败:

    Numbers([2, 3]).toString() // => Typed.List(Number)([ 2, 3 ])Numbers([1, 2, 3, "4", "5"]) // => TypeError: Invalid value:"4" is not a numberNumbers([1, 2, 3]).push(null) // => TypeError: Invalid value:"null" is not a number

    类型列表也可以命名为方便:

    var Strings =List(String, "Strings")Strings.of("hello", "world").toString() // => Strings(["hello","world" ])

    列表可以是一个复杂的特定记录类型&记录也可以具有类型列表的字段:

    var Points =List(Point, "Points")Points().toString() // => Points([])ps =Points.of({x:3}, {y:5}).toString()ps.toString() // => Points([ Point({"x": 3,"y": 0 }), Point({"x": 0,"y": 5 }) ])'ps.get(0) instanceofPoint// => trueps.get(1) instanceofPoint// => trueps.get(0).x// => 3ps.get(1).y// => 5ps.push({z:4, x:-4}).toJSON() // => [ { x: 3, y: 0 }, { x: 0, y: 5 }, { x: -4, y: 0 } ]Points(ps.toJSON()).equals(ps) // => true
    映射列表向其他类型列表

    关于列表的一些问题是,当它们强制转换某些类型时,它们也可以简单地转换为它的他类型:

    ps =Points.of({x:1}, {x:2})
    xs =ps.map(p=>p.x)ps.toString() // => Points([ Point({"x": 1,"y": 0 }), Point({"x": 2,"y": 0 }) ])xs.toString() // => Typed.List(Number)([ 1, 2 ])

    从示例中可以看到,上面 原始的ps 列表是 Point 记录,映射的xs 列表是数字,这是列表中的refleced。 这是一个映射函数,映射函数将返回所有类型的值,但是映射的结果是所有类型的映射都是联合类型的联合类型。

    映射

    通过提供键的类型和值的类型,可以定义类型化映射:

    var {Map, Record} =require("typed-immutable")var Product =Record({name:String}, "Product")var Products =Map(Number, Product)Products().toString() // 'Typed.Map(Number, Product)({})'Products([[1, {name:"Mapper 1000"}]]).toString() //Typed.Map(Number, Product)({ 1: Product({"name":"Mapper 1000" }) })

    类型映射只能包含具有指定类型的键和值的条目:

    Products([[1, "Mapper 1000"]]) // => TypeError: Invalid value: Invalid data structure"Mapper 1000" was passed to ProductProducts().set("P1", {name:"Mapper 1000"}) // TypeError: Invalid key:"P1" is not a number// All keys in an object are strings, so this fails too:Products({1: {name:"Mapper 1000"}}) // TypeError: Invalid key:"1" is not a number 

    注意最后一个示例- 对象中的所有键都是字符串,因这里如果从对象中实例化映射必须是字符串( 或者处理字符串的东西)。

    其他类型的映射一样,也可以为方便命名而命名:

    var Products =Map(Number, Product, "Products")Products([[1, {name:"Mapper 1000"}]]).toString() // Products({ 1: Product({"name":"Mapper 1000" }) })

    用户定义类型

    如 上面 部分所示,我们使用它的他类型的libary支持大多数的JS类型,并提供更多的复杂情况。

    JS本机类型

    你可以使用内置的BooleanNumberStringRegExp JS构建这些类型的结构。

    可能是

    可以使用 Maybe 定义可选类型,该类型将生成一个类型为 undefinednull 或者提供类型的值的类型:

    var {Maybe} =require("typed-immutable")var Color =Record({
     red:Number(0),
     green:Number(0),
     blue:Number(0),
     opacity:Maybe(Number)
    })Color().toJSON() // => { red: 0, green: 0, blue: 0, opacity: null }Color({red:200, opacity:80}).toJSON() // => { red: 200, green: 0, blue: 0, alpha: 80 }Color({red:200, opacity:"transparent"}) // => TypeError: Invalid value for"opacity" field://"transparent" is not nully nor it is of Number type
    联合

    联合类型是将许多不同类型组合在一起的一种方法。 这样可以创建可以采用以下几种类型之一的列表或者记录字段:

    var {Union} =require("typed-immutable")var Form =Record({
     user:Union(Username, Email),
     password:String('')
    })var form =Form()form.set('user', Username('gozala'))form.set('user', Email('gozala@mail.com'))
    自定义类型

    库允许你声明自定义类型,这样你就可以在定义具有记录和列表的更复杂类型时使用这些自定义类型:

    var {Typed} =require("typed-immutable")varRange= (from, to=+Infinity) =>Typed(`Typed.Number.Range(${from}..${to})`, value=> {
     if (typeof(value) !=='number') {
     returnTypeError(`"${value}" is not a number`)
     }
     if (!(value >= from && value <= to)) {
     returnTypeError(`"${value}" isn't in the range of ${from}..${to}`)
     }
     return value
     })var Color =Record({
     red:Range(0, 255),
     green:Range(0, 255),
     blue:Range(0, 255)
    })Color({red:20, green:20, blue:20}).toJSON() // => { red: 20, green: 20, blue: 20 }Color({red:20, green:20, blue:300}) // => TypeError: Invalid value for"blue" field://"300" isn't in the range of 0..255Color() // => TypeError: Invalid value for"red" field://"undefined" is not a numbervar Color =Record({
     red:Range(0, 255)(0),
     green:Range(0, 255)(0),
     blue:Range(0, 255)(0)
    })Color().toJSON() // => { red: 0, green: 0, blue: 0 }

    事实上,Typed 包含了一些它的他类型,包括与上面的示例类似的Typed.Number.Range

    任何类型

    尽管这种情况仍然存在,但在使用 Any 类型时仍然有很好的解决方案。 此外,在about列表映射列表about可以映射到任意类型,并且在某些情况下映射是 List(Any):

    var {Any} =require("typed-immutable")var Box =Record({value: Any})var v1 =Box({value:5})var v2 =v1.set("value", "hello")var v3 =v2.set("value", v2)v1.toString() // => Typed.Record({value: Any})({"value": 5 })v2.toString() // => Typed.Record({value: Any})({"value":"hello" })v3.toString() // => Typed.Record({value: Any})({"value": Typed.Record({value: Any})({"value":"hello" }) })

    捐赠

    • 在构建的代码中运行测试之前,在 npm test 之前运行 npm start

    许可证

    MIT许可证


    数据  str  type  Struct  Typed  immutable  
    相关文章