sylvainpolletvillard / ObjectModel

Strong Dynamically Typed Object Modeling for JavaScript
http://objectmodel.js.org
MIT License
467 stars 30 forks source link

Problem Extending Typescript Class #136

Closed nabriski closed 3 years ago

nabriski commented 3 years ago

Hi, this code appears to throw a runtime error in Typescript:

import {Model} from "objectmodel";

 class UserModel extends Model({
    email:String
}){

    constructor({email}){
        super({email});   
    }

    email:string
 };

const koko:UserModel= new UserModel({"email":"koko@jumbo.com"}); 
(koko as any).email = 123;

Error:

ts-node index.ts

/Users/inabriski/projects/test/index.ts:9
        super({email});
        ^
TypeError: _super.call is not a function
    at new UserModel (/Users/inabriski/projects/test/index.ts:9:9)
    at Object.<anonymous> (/Users/inabriski/projects/test/index.ts:15:23)
    at Module._compile (internal/modules/cjs/loader.js:1076:30)
    at Module.m._compile (/usr/local/lib/node_modules/ts-node/src/index.ts:814:23)
    at Module._extensions..js (internal/modules/cjs/loader.js:1097:10)
    at Object.require.extensions.<computed> [as .ts] (/usr/local/lib/node_modules/ts-node/src/index.ts:817:12)
    at Module.load (internal/modules/cjs/loader.js:941:32)
    at Function.Module._load (internal/modules/cjs/loader.js:782:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
    at main (/usr/local/lib/node_modules/ts-node/src/bin.ts:226:14)

Not sure if I'm doing something incorrectly or this is an actual problem but I didn't find an answer in the docs.

Thanks, Itamar

sylvainpolletvillard commented 3 years ago

Hi,

Except the static class field declaration, this is mostly plain JavaScript. Converting to JS, I got this:

import { Model } from "objectmodel";
class UserModel extends Model({
    email: String
}) {
    constructor({ email }) {
        super({ email });
    }
}
;
const koko = new UserModel({ "email": "koko@jumbo.com" });
koko.email = 123;

which executes fine: image

So the issue is probably in your compiler or runner. I'm not familiar with ts-node, do you know in which environment it runs ?

sylvainpolletvillard commented 3 years ago

ESM support in ts-node is marked as experimental: https://github.com/TypeStrong/ts-node/issues/1007 ; this may be linked

nabriski commented 3 years ago

Thanks, checked and this also happens with the Typescript compiler (4.1.3). This is the JS it generates:

"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
exports.__esModule = true;
var objectmodel_1 = require("objectmodel");
var UserModel = /** @class */ (function (_super) {
    __extends(UserModel, _super);
    function UserModel(_a) {
        var email = _a.email;
        return _super.call(this, { email: email }) || this;
    }
    return UserModel;
}(objectmodel_1.Model({
    email: String
})));
;
var koko = new UserModel({ "email": "koko@jumbo.com" });
koko.email = 123;

I suspect it has something to do with the Typescript bindings. Maybe Model is somehow not exported as a "proper" ts class? It appears ts relies on it having this _super function.

sylvainpolletvillard commented 3 years ago

It looks like your TypeScript setup is currently configured to output ES5 code. This library relies on ES6 proxies, and therefore does not work after being transpiled to ES5. What you call "proper TS classes" are actually a JavaScript feature and part of ES6 as well.

Could you try again with "target": "es6" in your tsconfig.json ?

nabriski commented 3 years ago

You are correct, setting the target to ES6:

tsc --target ES6 --moduleResolution node index.ts

It now compiles to:

import { Model } from "objectmodel";
class UserModel extends Model({
    email: String
}) {
    constructor({ email }) {
        super({ email });
    }
}
;
const koko = new UserModel({ "email": "koko@jumbo.com" });
koko.email = 123;

Which runs well:

TypeError: expecting email to be String, got Number 123
    at file:///Users/inabriski/projects/test/index.js:11:12
    at ModuleJob.run (internal/modules/esm/module_job.js:140:23)
    at async Loader.import (internal/modules/esm/loader.js:165:24)
    at async Object.loadESM (internal/process/esm_loader.js:68:5)

Thanks!