0

I am in the process of converting some of the old ES5 projects to TypeScript. In these projects, there are old-style classes (functions) defined as follows (simplified examples):

function MyClass(arg1) {
  this.arg_prop_1 = arg1;
  this.arg_prop_2 = function() { return this.arg_prop_1; }
}

MyClass.prototype.proto_prop = function() { return this.arg_prop_2() === 42; }

const obj = new MyClass(42);
console.assert(obj.proto_prop());
 

What is the correct / recommended / best way to type this code without actually changing it to ES6 class syntax?



What I have tried:

To enable TypeScript, I have defined some declarations as follows (not working):

interface MyClassThis {
  arg_prop_1: number;
  arg_prop_2: () => number;
  proto_prop: () => boolean;
}

interface MyClassCtor {
  new (arg1: number): MyClassThis;
}

To begin with, I just followed some code for "Date" type from here. As the above is not working (error: 'new' expression, whose target lacks a construct signature, implicitly has an 'any' type.(7009)), I'm not completely sure what's the right way to define types for such old-style class functions.

mg007
  • 2,888
  • 24
  • 29
  • Is leaving that file as `.js` and writing a `.d.ts` provides types for it an option? – Alex Wayne Feb 21 '23 at 18:05
  • Technical nit: it's not an "ES5 class", it's a an object with a prototype definition. You could call it an object type, but unless you're using the `class` keyword, it's not a class (even if, functionally, we can describe it as such). That's nitpicking, of course, but then this is a technical question =) – Mike 'Pomax' Kamermans Feb 21 '23 at 18:09
  • 1
    I think `.ts` code will never support this directly (see [ms/TS#2299](https://github.com/microsoft/TypeScript/issues/2299)). [This approach](https://tsplay.dev/mq9yYm) is probably the closest you can get; does that fully address the question? If so I could write up an answer; if not, what am I missing? – jcalz Feb 21 '23 at 18:18
  • Thanks @jcalz! This is a great pointer to an official community discussion. Although doesn't completely resolve my particular problem, I think you should post it to help others who are searching for some workaround without updaing hundreds of es5 classes :) – mg007 Feb 21 '23 at 20:41
  • ...ahm ahm ...without updating hundreds of "objects with a prototype definition" :) – mg007 Feb 21 '23 at 20:42

1 Answers1

1

If you can't change the source code, then leave it as .js and provide a .d.ts file that provides types for the file.

// my-class.js
export function MyClass(arg1) {
  this.arg_prop_1 = arg1;
  this.arg_prop_2 = function () {
    return this.arg_prop_1;
  };
}

MyClass.prototype.proto_prop = function () {
  return this.arg_prop_2() === 42;
};
// my-class.d.ts
export interface MyClass {
  arg_prop_1: string;
  arg_prop_2(): string;
  proto_prop(): boolean;
}

export const MyClass: {
  new (arg1: string): MyClass;
};

Now you can use it with those types:

import { MyClass } from "./my-class";

const instance = new MyClass("a string");
instance.proto_prop(); // boolean

See Code Sandbox

Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
  • Thanks, @AlexWayne! I don't think keeping an extension would be that straightforward in my scenario (undergoing a migration process js-> ts). Is there an option in tsconfig to make it work with ts+d.ts files? – mg007 Feb 21 '23 at 19:22
  • Then I would strongly suggest that rewriting this class be part of that migration process. I don't really know what this means, though "Is there an option in tsconfig to make it work with ts+d.ts files?" – Alex Wayne Feb 21 '23 at 19:36
  • That is a bit difficult as it'd require understaning and rewriting some of the most aged code. – mg007 Feb 21 '23 at 20:44
  • I wanted to understand why is it necessary to keep the extension as .js. Can't we get the same effect on .ts files by modifying some config options in tsconfig? – mg007 Feb 21 '23 at 20:46
  • If a file has `.ts` extension, then Typescript expects it to be fully type safe, and provide those types itself. But this file cannot be easily expressed safely as typescript without refactoring. You either have well typed Typescript, or you have untyped Javascript. You can't have it both ways. And Typescript is designed to be incrementally adopted so you don't have to convert every file all at once, and `.d.ts` files are one solution for exactly this problem. – Alex Wayne Feb 21 '23 at 21:01