2

Consider the following script

function caller(){
  getSpreadsheet1();// only the online IDE understands this
  getSpreadsheet2();// only the local IDE (VS Code) understands this
}

/**
 * @returns {SpreadsheetApp.Spreadsheet}
 */
function getSpreadsheet1(){
  var spreadsheet = SpreadsheetApp.openById('');
  return spreadsheet;
}

/**
 * @returns {GoogleAppsScript.Spreadsheet.Spreadsheet}
 */
function getSpreadsheet2(){
  var spreadsheet = SpreadsheetApp.openById('');
  return spreadsheet;
}

If you paste this script into the (new) Google Apps Script IDE, placing a period (.) after getSpreadsheet1() will trigger the IDE to display the member functions of class Spreadsheet, like addEditor(), for the sake of auto-completion. That is to say, the online IDE understands the type of getSpreadsheet1().

I have tried to achieve the same behaviour locally. I copied the script to a local file and I ran npm i -S @types/google-apps-script in the same folder, as per this guide. However, I get no autocompletion when placing a period after getSpreadsheet1(), but instead I only get it after placing the period after getSpreadsheet2().

Note that my local IDE (VS Code) does understand the type of SpreadsheetApp.openById(''), so that does seem to work correctly.

Is there a way to get my local IDE to understand the JSDoc comments that use the types like SpreadsheetApp.Spreadsheet required by the online IDE?

lepsch
  • 8,927
  • 5
  • 24
  • 44
  • It seems my issue can be solved by using the extension ".ts" instead of ".js" for the local file. That is not ideal for me, as `clasp pull` (see [clasp](https://developers.google.com/apps-script/guides/clasp)) will create ".js" files. So I am still hoping it is possible to achieve this in a different way – Jacob Akkerboom Aug 26 '22 at 16:14
  • It seems online IDE uses a different type definitions than the community provided one @types/google-apps-script – TheMaster Aug 27 '22 at 07:30
  • But both IDEs work, if you remove the `@return` and leave it without any type. – TheMaster Aug 27 '22 at 07:40
  • if I recall correctly, the definitions are the same (or at least were pulled initially from the DT repository), however the main namespace (`GoogleAppsScript`) is "unwrapped". – Oleg Valter is with Ukraine Aug 27 '22 at 11:49
  • @OlegValteriswithUkraine Even if you remove `google-apps-script`, It's not even the same key. `SpreadsheetApp.Spreadsheet`(which shows it's a `interface` in apps script online IDE) vs `Spreadsheet.Spreadsheet` – TheMaster Aug 27 '22 at 12:02
  • @TheMaster that's true, I believe the "App" parts are aliased too. However, I can't say for sure if that's the only difference. – Oleg Valter is with Ukraine Aug 27 '22 at 12:29
  • @OlegValteriswithUkraine If it's a alias(`SpreadsheetApp` => `Spreadsheet`), that would be fine. But `Spreadsheet.Spreadsheet` isn't even recognized. In online IDE, the namespace for sheets related interfaces is `SpreadsheetApp`. For eg, `SpreadsheetApp.` in jsdoc lists various options like `SpreadsheetApp.Range` instead of ``SpreadsheetApp.Spreadsheet.Range``( which isn't listed or recognized, when used) – TheMaster Aug 27 '22 at 12:39
  • @TheMaster I just found that adding `declare namespace SpreadsheetApp {type Spreadsheet = GoogleAppsScript.Spreadsheet.Spreadsheet;}` at the start of the file `node_modules/@types/google-apps-script/google-apps-script.spreadsheet.d.ts` makes things work for ".js" files locally in the same way that they work in the online IDE. Hopefully this leads to a nice solution – Jacob Akkerboom Aug 27 '22 at 16:25
  • @JacobAkkerboom I think it'll fail with `range`. Online IDE uses: `SpreadsheetApp.Range`, while local uses `GoogleAppsScript.Spreadsheet.Range`. – TheMaster Aug 27 '22 at 16:43
  • @TheMaster ah yes agreed, the namespace `SpreadsheetApp` I define is incomplete. We could add `type Range = GoogleAppsScript.Spreadsheet.Range` in it and other definitions for `Sheet` etc etc. I was just now looking if maybe it would be possible to "alias a namespace", to maybe fix everything with a single line of typescript code instead of having many aliases, but I am new to typescript. – Jacob Akkerboom Aug 27 '22 at 16:52
  • Jacob, @OlegValteriswithUkraine said the same. I thought the interface is wrong with many nested levels obliterated. But maybe I am wrong. See [this](https://stackoverflow.com/questions/53198823/abbreviate-lengthy-typescript-types) for alias – TheMaster Aug 27 '22 at 16:57
  • @TheMaster I just tested it for `Range`, adding it to my interface seems to work. But yeah it is hard to be sure that everything is the same. It looks like I am using `type` in the same way as in the answer in the Q&A you link to. I was hoping to use something like `type SpreadsheetApp = GoogleAppsScript.Spreadsheet` so that `SpreadsheetApp` is a namespace that is an alias of namespace `GoogleAppsScript.Spreadsheet`, but that it seems that is not possible. Maybe it is more straightforward to just directly define `SpreadsheetApp`, replacing the definition of `GoogleAppsScript.Spreadsheet`. – Jacob Akkerboom Aug 27 '22 at 17:10

1 Answers1

2

Besides removing the @returns completely to let Typescript infer the result type automatically it's possible to make it work in both cases with the following trick:

function caller() {
    getSpreadsheet1();// only the online IDE understands this
    getSpreadsheet2();// only the local IDE (VS Code) understands this
}

/**
 * @typedef {ReturnType<typeof SpreadsheetApp['openById']>} Spreadsheet
 */

/**
 * @returns {Spreadsheet}
 */
function getSpreadsheet1() {
    var spreadsheet = SpreadsheetApp.openById('');
    return spreadsheet;
}

/**
 * @returns {Spreadsheet}
 */
function getSpreadsheet2() {
    var spreadsheet = SpreadsheetApp.openById('');
    return spreadsheet;
}
lepsch
  • 8,927
  • 5
  • 24
  • 44
  • 1
    Thank you, that is an interesting trick. I just found that adding `declare namespace SpreadsheetApp {type Spreadsheet = GoogleAppsScript.Spreadsheet.Spreadsheet;}` at the start of the file `node_modules/@types/google-apps-script/google-apps-script.spreadsheet.d.ts` makes things work for ".js" files locally in the same way that they work in the online IDE. I am still processing that new information, I am not sure how that can be used to create an efficient workflow – Jacob Akkerboom Aug 27 '22 at 16:31