Repository: gcanti/tcomb (js)

Issue:
[Question/Bug] How to use fromJSON and defaultProps
### Version

3.2.24

### Expected behaviour

Allow use custom deserialize functions and the defaultProps

### Actual behaviour

the fromJSON function is not using the defaultProps from t.struct

### Steps to reproduce

```js
const t = require('tcomb');
const fromJSON = require('tcomb/lib/fromJSON');

t.Date.fromJSON = function(x) {
  return isNaN(Date.parse(x)) ? x : new Date(x);
};

const Sample = t.struct(
  {
    date: t.Date
  },
  {
    defaultProps: {
      get date() {
        return new Date();
      }
    }
  }
);

const sample = Sample({});
const json = JSON.parse(JSON.stringify(sample));
console.log(fromJSON(json, Sample));
console.log(fromJSON({}, Sample));
```


### Stack trace and console log

```
➔ node date.test.js
Struct { date: 2018-02-22T21:16:17.823Z }
/home/rkmax/my_project/node_modules/tcomb/lib/fail.js:2
  throw new TypeError('[tcomb] ' + message);
  ^

TypeError: [tcomb] Invalid value undefined supplied to Struct{date: Date}/date: Date
    at Function.fail (/home/rkmax/my_project/node_modules/tcomb/lib/fail.js:2:9)
    at assert (/home/rkmax/my_project/node_modules/tcomb/lib/assert.js:14:12)
    at Irreducible (/home/rkmax/my_project/node_modules/tcomb/lib/irreducible.js:18:7)
    at create (/home/rkmax/my_project/node_modules/tcomb/lib/create.js:9:104)
    at fromJSON (/home/rkmax/my_project/node_modules/tcomb/lib/fromJSON.js:26:12)
    at fromJSON (/home/rkmax/my_project/node_modules/tcomb/lib/fromJSON.js:61:20)
    at Object.<anonymous> (/home/rkmax/my_project/date.test.js:25:13)
    at Module._compile (module.js:643:30)
    at Object.Module._extensions..js (module.js:654:10)
    at Module.load (module.js:556:32)

```


Pull request description:
fixes #312

Patch:
diff --git a/lib/fromJSON.js b/lib/fromJSON.js
index 7edc7cd..6da91e7 100644
--- a/lib/fromJSON.js
+++ b/lib/fromJSON.js
@@ -55,10 +55,15 @@ function fromJSON(value, type, path) {
         });
       }
       var props = type.meta.props;
+      var defaultProps = type.meta.defaultProps;
       ret = {};
       for (k in props) {
+        var actual = value[k];
+        if (actual === undefined) {
+          actual = defaultProps[k];
+        }
         if (props.hasOwnProperty(k)) {
-          ret[k] = fromJSON(value[k], props[k], ( process.env.NODE_ENV !== 'production' ? path.concat(k + ': ' + getTypeName(props[k])) : null ));
+          ret[k] = fromJSON(actual, props[k], ( process.env.NODE_ENV !== 'production' ? path.concat(k + ': ' + getTypeName(props[k])) : null ));
         }
       }
       return new type(ret);


Test patch:
diff --git a/test/fromJSON.js b/test/fromJSON.js
index 0b56a29..24f7080 100644
--- a/test/fromJSON.js
+++ b/test/fromJSON.js
@@ -98,6 +98,31 @@ describe('fromJSON', function () {
     }, '[tcomb] Invalid value undefined supplied to MyType/name: String');
   });
 
+  it('should handle struct with options.defaultProps', function () {
+    var MyType = t.struct({
+      surname: t.String,
+      number: t.Number
+    }, {
+      name: 'MyType',
+      defaultProps: {
+        surname: 'Canti',
+        get number() {
+          return 2;
+        }
+      }
+    });
+
+    var source = {};
+    var expected = {
+      surname: 'Canti',
+      number: 2
+    };
+    var json = jsonify(source);
+    var actual = fromJSON(json, MyType);
+    assert.ok(actual instanceof MyType);
+    assert.deepEqual(actual, expected);
+  });
+
   it('should handle interface', function () {
     var MyType = t.interface({
       name: t.String,