3

Suppose we have 2 simple TypeScript classes in 2 separate files:

  • B.ts:
namespace A{
 export abstract class ItemBase {
  id:number=432;
 }
}
  • C.ts
///<reference path="B.ts"/>
namespace A{
 export class ItemType extends A.ItemBase{}
}
  • and invoking code in Code.ts:
///<reference path="C.ts"/>
let a:A.ItemType=new A.ItemType();

Everything works well after pushing it using clasp

But if I change the name of the C.ts file to AA.ts, I would get the error:

TypeError: Cannot read property "prototype" from undefined. (row 14, file „AA”).

The problem even exists if I would not instantiate the ItemType class in Code.ts.

It seems that ts2gas doesn't take into consideration the extends keyword during the code transpiling and sets the output gs files as corresponding ts files order. So if we name the extended class file before the class file that we were extending from (in alphabetical order) we would get an error.

Do I have to take care of proper order of ts filenames during development? Do I have to attach some sort of mechanism that takes care of gs file loading order? It seems to be redundant for me while the gs files have been already transpiled. The transpilation process (ts2gas) should take care of proper class extension strategy that was used in TypeScript manner. If ts2gas can transpile the TypeScript Class into JS OOP, using prototyping, why it can't handle the class extensions properly?

I suppose there's some simpler and better way.

emilll
  • 85
  • 6
  • OK, I found this problem as an Issue on GitHub [ts2gas](https://github.com/grant/ts2gas/issues/27). They stands that there is no problem with ts2gas but with the clasp, which exposes the files to ts2gas in not proper order. – emilll Jan 18 '20 at 17:30
  • Also there are some opened issues at the moment on GitHub @google/clasp: – emilll Jan 18 '20 at 17:31
  • [527](https://github.com/google/clasp/issues/527),[718](https://github.com/google/clasp/issues/718),[624](https://github.com/google/clasp/issues/624). And still there is opened [pull request](https://github.com/google/clasp/pull/582) for that. – emilll Jan 18 '20 at 17:37

2 Answers2

6

One of the root cause of your issue is the Rhino v1.7R3 JavaScript engine which runs Apps Script and the way the many .gs files making up one script are hoisted.

Also the way ts2gas library works, by transpiling each source file independently, has some limitations and may play a minor role in your issue.

To summarise, your script is effectively the concatenation of all your .gs files, in the order they were appear in the Google Apps Script editor. This order is usually the order each .gs was created. When using @google/clasp to push your local files to your script project, this order is likely to change to the alphabetical order of your source filenames.

In your example, the .gs declaring the parent class must appear before any declaration of children classes (i.e. the hoisting issue)

To ensure proper ordering of your code and avoid this, you have several options:

  1. regroup code with potential hoisting issue in a single file (clumsy and messy in the long run)
  2. use the filepushorder option in your .clasp.json to specify which files should be pushed first. (easy to setup and maintain)
  3. have a different project build chain for finer control of Typescript compilation and precise file order (if necessary.) Here is the template repository I have setup to get started.

EDIT

To illustrate usage of the filePushOrder option, I have setup a sample repository with your sample code.

PopGoesTheWza
  • 558
  • 4
  • 12
  • You've mentioned:"_the concatenation of all your .gs files,_ **_in the order they were appear in the Google Apps Script editor. This order is usually the order each .gs was created_**". So if I explicitly set the order of files using _filePushOrder_ flag, should there be a reflection of this in the order of .gs files in Apps Script editor? I've checked it and it's not. .gs files are ordered differently than my declared push order. But the main thing is that, that above example **started to work**. So the order of presented .gs files has nothing to do with the _filePushOrder_ paradigm. – emilll Jan 20 '20 at 03:03
  • Even after using _filePushOrder_ .gs files order in App Script editor is presented in some unclear strategy. Definitely it is not _filePushOrder_ strategy and even it is not alphabetical strategy right now. – emilll Jan 20 '20 at 03:15
  • 1
    with `"filePushOrder": ["B.ts", "AA.ts"]` in your `clasp.json`, the files from your example should be correctly reordered after a `clasp push`. The `clasp status` command is also a quick way to check which files will be pushed and in which order. – PopGoesTheWza Jan 20 '20 at 16:09
  • Hello! Let me add that if your file is in a subfolder, for example `src`, you need to specify its full relative path: `"filePushOrder": ["src/B.ts", "srcAA.ts"]`, otherwise it will fail without warning. – coccoinomane Aug 30 '20 at 10:32
0

Indeed, as @PopGoesTheWza indicated in the accepted answer, the alphabetical ordering of your files makes a difference. During my testing, I simply renamed my abstract class from MyClass.gs to aMyClass.gs, directly from the App Script editor and that didn't work.

So I went back to my Visual Studio project to rename it there and wondered if using a subfolder that started with a, would work. So I created an /Abstract folder and moved the MyClass.ts file in there. That did the trick in my case.

That solution seemed much more elegant, so I've just started using that pattern for any abstract classes I develop in the future.

Admittedly, there may be more nuanced problems that this does not solve. An abstract class that extends another abstract class, for instance. Then you could still run into this problem. But if your class structure is that complex, you should probably expect to deal with a little file ordering strategy from the start.

Todd Powers
  • 127
  • 1
  • 7