jmtoung
jmtoung

Reputation: 1012

Typescript: Is it possible to declare a class using function

Reading this tutorial, I see that one way to declare a class in Typescript is to use the class syntax like so.

class Person { 
   constructor(name: string) {
      this.name = name
   }
}

And upon compiling it will generate the following Javascript code.

//Generated by typescript 1.8.10
var Person = (function () {
   function Person(name) {
      this.name = name
   }
   return Person;
}());

My question is, is it possible to declare in Typescript a class using the generated Javascript syntax like so...

function Person(name: string) {
  this.name = name
}

Upvotes: 1

Views: 692

Answers (2)

smac89
smac89

Reputation: 43078

As an improvement to the already existing answer, you can also do it this way:

interface Person {
    name: string;
}

interface PersonConstructor {
    new(name: string): Person;
}

const Person: PersonConstructor = function (this: Person, name: string) {
    this.name = name;
} as any;

const p = new Person("Alice");
console.log(p.name); // Alice

This way you atleast preserve some type safety if you still care about that stuff.


This way also allows you to define static methods on Person by first adding them to PersonConstructor. So you can do:

interface PersonConstructor {
    new(name: string): Person;
    foo(bar: string): void;
}

And later on do:

Person.foo = (bar: string) => {
    console.log(`foo ${bar}`);
}

And call it with Person.foo("something");

Playground Link

Upvotes: 1

jcalz
jcalz

Reputation: 327819

It's sort of possible, but not recommended, according to microsoft/TypeScript#2310. According to the TS team, if you're using TypeScript, you should use class directly. This will compile as-is if you target ES2015 or above, or it will compile down to the function you mentioned if you target ES5.

If you want to ignore the recommendation, you have to do some type annotations and assertions to get the behavior you want, and it will end up leaving a little but of runtime no-op code in the compiled output. And it won't be particularly type safe (if you make a mistake in your typings the compiler will not complain).

First you have to manually define the interface of your class instance, since the compiler can't infer it for you:

interface Person {
    name: string;
}

Then you have to give your function a this parameter so the compiler understands that, inside the implementation of your function, the this value should be treated as an instance of your class. You should also rename the function out of the way from Person to something else (like _Person), because you won't be able to change its type to something new-able after the fact:

function _Person(this: Person, name: string) {
    this.name = name;
}

Finally, you can define Person as a const whose value is the same as your renamed function at runtime, but at compile-time you give it a constructor signature:

const Person = _Person as any as new (name: string) => Person;

Now it's set up so that Person is a function at runtime (not a class, even if you target ES2015+), but that the compiler sees it as a constructor:

const p = new Person("Alice");
console.log(p.name); // Alice

So, uh, hooray? Fighting against the compiler like this and "winning" probably isn't worth it in any production setting. But it's interesting to see how to do it, I guess.

Okay, hope that helps; good luck!

Playground link to code

Upvotes: 2

Related Questions