Use Typescript with Google Apps Script
Asked Answered
S

6

18

I'd like to use Typescript for my Google Apps Script (GAS) projects, but I can't find a way to compile my code into something that GAS accepts.

GAS doesn't support export, and Typescript seems to dislike accessing variables via the global scope (it wants imports/require, and thus exports).

I'm looking for any of the following solutions which I believe would make things work for me:

1) Babel plugin or the like that can remove all the Import and Export statements along with their attributed names (requires that I don't use the same method names, which I don't.

So:

import MyLibrary from './library';
export function greetJohn() { MyLibrary.greet('John'); }
export default { greetJohn }

Becomes:

function greetJohn() { greet('John'); }

2) Change typescript so that it can see the global scope

3) Babel plugin or the like that combines all the .ts files into one .js file and transforms the import/export statements by treating each file like an Object/function.

Scriptorium answered 14/2, 2018 at 16:26 Comment(1)
You don't have to use import, export with Typescript if you don't want to... Typescript can see the global scope just like Javascript can. It's not clear from your question why you use export ?Generalist
H
9

Now it's possible to use Typescript to develop Google Apps Script projects but this could not be done directly on the Apps Script Editor.

According to Develop Apps Script using TypeScript the easier way is by using clasp.

Related

Hudis answered 22/8, 2018 at 3:1 Comment(0)
M
12

Background

Things have changed since you wrote this question and there is a better support for TypeScript for AppsScript development nowadays. Like Aliabbas Merchant said, the best way to compile TS to GAS is using clasp, which is a CLI tool for developing AppsScript apps. Clasp uses ts2gas library under the hood to convert TS to GAS, so there is no more need for Webpack or any other bundler just for converting the code into AppsScript.

What Aliabbas Merchant didn't mention is that ts2gas doesn't (yet!) support export syntax, which causes linter errors when developing, and won't be accepted nicely by the IDE (even if disabling the linter, the IDE will not recognize the imported vars and won't consider them as a reference to the exports...)

The Problem

The problem starts from the fact that AppsScript doesn't use modules system and every top-level var that is defined is also accessible from other files (in contrast to TS that "wrapps" files as modules). The problem with the current version of ts2gas is that it transpiles the following statement:

export var x = 5;

into:

exports.x = 5

So, after clasp converts .ts to .gs, when another file of .gs tries to access the exported var, it doesn't find it. Actually, this var isn't stored as a global var like expected, but inside the exports object (which is itself a global var). If we could "cheat" the compiler somehow, so it both keeps the var in the global env, even though it is also exported (so we can work with TS with no errors), we would win.

Solutions

So we're looking for a workaround. You can find some in clasp's using TS docs (https://github.com/google/clasp/blob/master/docs/typescript.md), but there is another trick that I've found the most simple:

Defining the var locally (without exporting it), and later on exporting an object with all the vars (which could be also functions, of course) we want to export. TS will behave as exporting the vars regularly (it's a syntactic sugar for exporting vars), but the compiler will keep the vars both global and inside an exports object.

Example

const a = 5;

function b() {
   return 'Hello World';
}

export {
   a,
   b
}

This way, the vars are truly exported and can be used in TS as usual, but would also stay at the global env after compiling to GAS files (the compiler would actually add them to the export object, but we shouldn't care about it).

Morten answered 26/7, 2020 at 11:22 Comment(4)
This is the solution I was searching for. A way to have the balance between what AppScript expects and local development. Thanks a lotSmolensk
This is not working. I'am getting a reference error in google sheets when executing my script.Godchild
Yes, because TS transforms the imported identifier, so that it does not match the globally available one anymore. I don't know how it worked in 2020 and how to make it work now, though.Domingo
I added a workaround below.Busby
H
9

Now it's possible to use Typescript to develop Google Apps Script projects but this could not be done directly on the Apps Script Editor.

According to Develop Apps Script using TypeScript the easier way is by using clasp.

Related

Hudis answered 22/8, 2018 at 3:1 Comment(0)
M
6

I've been looking into a similar scenario this past couple of weeks (not using TypeScript but still ES6/ES7).

Some of the things I've found that you might find helpful for what you're trying to achieve:

  • GAS webpack plugin allows you to use webpack for module loading within GAS by detecting when you're assigning to the global object and then generating a top level function which GAS can run. This means your import and exports will all be handled by webpack so you don't have to remove them.
  • I wasn't able to get import * as x from y syntax to work however import { x } from y and import x from y worked fine when using webpack.
  • You can include your HTML as a string in your bundle using HTML loader.

If you don't want to use webpack, one solution is to put all your code in a single app.ts file, create an object containing all of your functions, set the functions to be top-level so they can be picked up by GAS. You could also export the container object and use it in a test suite. When you compile with Babel use the babel-plugin-transform-remove-export plugin to remove the export statement.

const app = {
  onInstall: () => { ...
  },
  onOpen: () => { ...
  }
}

const { onOpen, onInstall } = app;

export { app };
Mcnew answered 15/2, 2018 at 9:27 Comment(1)
Great answer. I ended up combining all .ts files into one file. It's a bit of a beast, but it's just easier for me than messing with WebPack or trying to sneak around what GAS is doing.Bloodfin
M
2

The best (and recommended) way of using TypeScript with Google App Script is to use clasp.

  1. Install clasp: npm install @google/clasp -g
  2. Login: clasp login
  3. Create a project: clasp create [scriptTitle]
  4. Optional: Change the timezone in the appsscripts.json file to the one most suitable from this list.
  5. Install the types: npm i -S @types/google-apps-script
  6. Create the tsconfig.json file:
    { "compilerOptions": { "lib": [ "esnext" ], "experimentalDecorators": true } }
  7. Write your code in .ts files
  8. Push your code using clasp push
  9. Refresh your script page and run your function(s).

Note: You don't need to use import and export. Variables can be accessed across files.

Messier answered 7/3, 2020 at 7:40 Comment(0)
B
0

The answer from shay-yzhakov no longer works in 2024. The problem is a bug in ts2gas that does not have a fix at the current time.

The workaround for me was to convert all of my functions to classes.

Prior Code

util.js

function func1(input: string): void {
  // code
}

function func2(input: string): void {
  // code
}

Old import and method calls:

import { func1, func2 } from './util';

func1('a');
func2('b');

New, working code with classes

util.js

class Util {
  func1(input: string): void {
    // code
  }

  func2(input: string): void {
    // code
  }

}

New import and method calls:

import { Util } from './util';

const util = new Util();
util.func1('a');
util.func2('b');
Busby answered 7/4 at 14:4 Comment(0)
M
0

A simple fix that seems to have worked for me nicely is by adding "allowJs": true to compilerOptions in tsconfig.json.

For example, my tsconfig.json looks like this:

{
  "compilerOptions": {
    "lib": ["esnext"],
    "types": ["google-apps-script"],
    "experimentalDecorators": true,
    "target": "ES2019",
    "allowJs": true,
    "outDir": "js"
  }
}

The only thing I've noticed is that my IDE still doesn't see the Date and String extensions I have on another file.

Misappropriate answered 5/7 at 14:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.