0

Given the following interfaces:

interface Person {
    constructor: Function;
    getFullName(): string;
}

interface PersonConstructor {
    new (firstName: string, lastName: string): Person;
    prototype: Person;
    createPerson(firstName: string, lastName: string): Person;
}

I want to custom-write a class using JS rather than TS, like so:

var Person = (() => {
    function Person(firstName: string, lastName: string) {
        var _firstName = firstName;
        var _lastName = lastName;

        this.getFullName = () => _firstName + " " + _lastName;
    }

    Person.createPerson = (firstName: string, lastName: string): Person => new Person(firstName, lastName);

    return Person;
})();

But I get the following error:

Property 'createPerson' does not exist on type '(firstName: string, lastName: string) => void'

I would also like to be able to use my custom JS as a base class in TypeScript...

class FamousPerson extends Person {

}

But I get this error...

Type '(firstName: string, lastName: string) => void' is not a constructor function type.

How do I get my custom JS to respect my interfaces?

Note: the interfaces follow the same constructor (static interface) pattern adopted in TS 1.4

2 Answers 2

1

You can make it work, but with some use of any:

interface Person {
    getFullName(): string;
}

interface PersonConstructor {
    new (firstName: string, lastName: string): Person;
    prototype: Person;
    createPerson(firstName: string, lastName: string): Person;
}

var Person = (function(): PersonConstructor {
    function Person(firstName: string, lastName: string) {
        var _firstName = firstName;
        var _lastName = lastName;

        this.getFullName = () => _firstName + " " + _lastName;
    }

    (Person as any).createPerson = (firstName: string, lastName: string): Person => new Person(firstName, lastName);

    return Person as any;
})();

(code in playground)

Changes from your code:

  1. Removed the constructor from the Person interface
  2. The actual constructor function closure is declared to return a PersonConstructor
  3. Casting Person to any when adding the createPerson function and when returning it

While this works for your first request, you'll get stuck with the second one, if you intend to override the Person class methods.
For example:

class User extends Person {
    constructor(firstName: string, lastName: string) {
        super(firstName, lastName);

        console.log(this.getFullName());
    }

    getFullName(): string {
        return "user: " + super.getFullName();
    }
}

Creating a new User will log: "first last" instead of "user: first last" and that's because the User.getFullName is defined on the prototype but then in the ctor of Person that is being overridden by the new definition.
It's clearer to see in the compiled js code:

var Person = (function () {
    function Person(firstName, lastName) {
        var _firstName = firstName;
        var _lastName = lastName;
        this.getFullName = function () { return _firstName + " " + _lastName; };
    }
    Person.createPerson = function (firstName, lastName) { return new Person(firstName, lastName); };
    return Person;
})();
var User = (function (_super) {
    __extends(User, _super);
    function User(firstName, lastName) {
        _super.call(this, firstName, lastName);
        console.log(this.getFullName());
    }
    User.prototype.getFullName = function () {
        return "user: " + _super.prototype.getFullName.call(this);
    };
    return User;
}(Person));

(code in playground)

The only way (without going through a lot of trouble) to be able to override methods is if they are implemented on the prototype and not on the instance.
But that will take away the ability to use the "private members" you defined with var, which I assume is the reason you want to do this to begin with.

Sign up to request clarification or add additional context in comments.

6 Comments

Exactly so. All I want really is a way to define closure based implementations with per-instance functions and real "private" variables in TypeScript, instead of everything being bound to the prototype. I hate the fact that Anders and his team are forcing their ideology on me!
"Being bound to the prototype" is what makes OO possible, otherwise you're in for a world of pain. I strongly recommend that you drop this approach as it will cause you to work way harder than is needed just so a few variables will be really "private".
In my experience this has never been as issue. I've achieved OO, inheritance and private members with very little effort, so unless I've massively overlooked something, that is a moot point I'm afraid
How do you override the getFullName method in your example?
...that old chestnut. It's easy until you need to make super calls, you only need to redefine getFullName in the sub-type, but when using closures, you can't call the super-type implementation (ar at least not to my knowledge)
|
0
  1. Replace Person.createPerson with `Person['createPerson']

  2. You cannot extends custom-writen class in JS. TypeScript wouldn't understand var Person is a class. Is there any reason you cannot write classes in TS?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.