Convert rest-client-behavior.js to .ts

This change shows how to import .js files from a .ts file.

Change-Id: I432c92fdb989f1f6e32ef95aa011c846ec2e9019
diff --git a/polygerrit-ui/app/.eslintrc.js b/polygerrit-ui/app/.eslintrc.js
index 785732a..cc9f304 100644
--- a/polygerrit-ui/app/.eslintrc.js
+++ b/polygerrit-ui/app/.eslintrc.js
@@ -183,6 +183,9 @@
         // The rule is required for .js files only, because typescript compiler
         // always checks import.
         "import/no-unresolved": 2,
+      },
+      "globals": {
+        "goog": "readonly",
       }
     },
     {
diff --git a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.ts b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.ts
index 05bf169..3b30665 100644
--- a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.ts
+++ b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.ts
@@ -14,160 +14,204 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {BaseUrlBehavior} from '../base-url-behavior/base-url-behavior.js';
-import {ChangeStatus} from '../../constants/constants.js';
+import {
+  BaseUrlBehavior,
+  BaseUrlBehaviorInterface,
+} from '../base-url-behavior/base-url-behavior';
+import {ChangeStatus} from '../../constants/constants';
+
+// WARNING: The types below can be completely wrong!
+// The types was added to avoid eslinter and typescript errors.
+// Correct typing requires more analysis and (probably) code changes.
+// This will be done later.
+type ChangeNum = string; // This can be wrong! See WARNING above
+type PatchNum = string; // This can be wrong! See WARNING above
+
+// This can be wrong! See WARNING above
+interface Change {
+  status: string; // This can be wrong! See WARNING above
+  mergeable: boolean; // This can be wrong! See WARNING above
+  work_in_progress: boolean; // This can be wrong! See WARNING above
+  is_private: boolean; // This can be wrong! See WARNING above
+  submittable: boolean; // This can be wrong! See WARNING above
+}
+
+// This can be wrong! See WARNING above
+interface ChangeStatusesOptions {
+  mergeable: boolean; // This can be wrong! See WARNING above
+  submitEnabled: boolean; // This can be wrong! See WARNING above
+}
 
 /** @polymerBehavior Gerrit.RESTClientBehavior */
-export const RESTClientBehavior = [{
-  ChangeDiffType: {
-    ADDED: 'ADDED',
-    COPIED: 'COPIED',
-    DELETED: 'DELETED',
-    MODIFIED: 'MODIFIED',
-    RENAMED: 'RENAMED',
-    REWRITE: 'REWRITE',
-  },
+export const RESTClientBehavior = [
+  {
+    ChangeDiffType: {
+      ADDED: 'ADDED',
+      COPIED: 'COPIED',
+      DELETED: 'DELETED',
+      MODIFIED: 'MODIFIED',
+      RENAMED: 'RENAMED',
+      REWRITE: 'REWRITE',
+    },
 
-  // Must be kept in sync with the ListChangesOption enum and protobuf.
-  ListChangesOption: {
-    LABELS: 0,
-    DETAILED_LABELS: 8,
+    // Must be kept in sync with the ListChangesOption enum and protobuf.
+    ListChangesOption: {
+      LABELS: 0,
+      DETAILED_LABELS: 8,
 
-    // Return information on the current patch set of the change.
-    CURRENT_REVISION: 1,
-    ALL_REVISIONS: 2,
+      // Return information on the current patch set of the change.
+      CURRENT_REVISION: 1,
+      ALL_REVISIONS: 2,
 
-    // If revisions are included, parse the commit object.
-    CURRENT_COMMIT: 3,
-    ALL_COMMITS: 4,
+      // If revisions are included, parse the commit object.
+      CURRENT_COMMIT: 3,
+      ALL_COMMITS: 4,
 
-    // If a patch set is included, include the files of the patch set.
-    CURRENT_FILES: 5,
-    ALL_FILES: 6,
+      // If a patch set is included, include the files of the patch set.
+      CURRENT_FILES: 5,
+      ALL_FILES: 6,
 
-    // If accounts are included, include detailed account info.
-    DETAILED_ACCOUNTS: 7,
+      // If accounts are included, include detailed account info.
+      DETAILED_ACCOUNTS: 7,
 
-    // Include messages associated with the change.
-    MESSAGES: 9,
+      // Include messages associated with the change.
+      MESSAGES: 9,
 
-    // Include allowed actions client could perform.
-    CURRENT_ACTIONS: 10,
+      // Include allowed actions client could perform.
+      CURRENT_ACTIONS: 10,
 
-    // Set the reviewed boolean for the caller.
-    REVIEWED: 11,
+      // Set the reviewed boolean for the caller.
+      REVIEWED: 11,
 
-    // Include download commands for the caller.
-    DOWNLOAD_COMMANDS: 13,
+      // Include download commands for the caller.
+      DOWNLOAD_COMMANDS: 13,
 
-    // Include patch set weblinks.
-    WEB_LINKS: 14,
+      // Include patch set weblinks.
+      WEB_LINKS: 14,
 
-    // Include consistency check results.
-    CHECK: 15,
+      // Include consistency check results.
+      CHECK: 15,
 
-    // Include allowed change actions client could perform.
-    CHANGE_ACTIONS: 16,
+      // Include allowed change actions client could perform.
+      CHANGE_ACTIONS: 16,
 
-    // Include a copy of commit messages including review footers.
-    COMMIT_FOOTERS: 17,
+      // Include a copy of commit messages including review footers.
+      COMMIT_FOOTERS: 17,
 
-    // Include push certificate information along with any patch sets.
-    PUSH_CERTIFICATES: 18,
+      // Include push certificate information along with any patch sets.
+      PUSH_CERTIFICATES: 18,
 
-    // Include change's reviewer updates.
-    REVIEWER_UPDATES: 19,
+      // Include change's reviewer updates.
+      REVIEWER_UPDATES: 19,
 
-    // Set the submittable boolean.
-    SUBMITTABLE: 20,
+      // Set the submittable boolean.
+      SUBMITTABLE: 20,
 
-    // If tracking ids are included, include detailed tracking ids info.
-    TRACKING_IDS: 21,
+      // If tracking ids are included, include detailed tracking ids info.
+      TRACKING_IDS: 21,
 
-    // Skip mergeability data.
-    SKIP_MERGEABLE: 22,
+      // Skip mergeability data.
+      SKIP_MERGEABLE: 22,
+
+      /**
+       * Skip diffstat computation that compute the insertions field (number of lines inserted) and
+       * deletions field (number of lines deleted)
+       */
+      SKIP_DIFFSTAT: 23,
+    },
+
+    listChangesOptionsToHex(...args: number[]) {
+      let v = 0;
+      for (let i = 0; i < args.length; i++) {
+        v |= 1 << args[i];
+      }
+      return v.toString(16);
+    },
 
     /**
-     * Skip diffstat computation that compute the insertions field (number of lines inserted) and
-     * deletions field (number of lines deleted)
+     *  @return {string}
      */
-    SKIP_DIFFSTAT: 23,
+    changeBaseURL(
+      this: BaseUrlBehaviorInterface,
+      project: string,
+      changeNum: ChangeNum,
+      patchNum: PatchNum
+    ) {
+      let v =
+        this.getBaseUrl() +
+        '/changes/' +
+        encodeURIComponent(project) +
+        '~' +
+        changeNum;
+      if (patchNum) {
+        v += '/revisions/' + patchNum;
+      }
+      return v;
+    },
+
+    changePath(this: BaseUrlBehaviorInterface, changeNum: ChangeNum) {
+      return this.getBaseUrl() + '/c/' + changeNum;
+    },
+
+    changeIsOpen(change?: Change) {
+      return change && change.status === ChangeStatus.NEW;
+    },
+
+    /**
+     * @param {!Object} change
+     * @param {!Object=} opt_options
+     *
+     * @return {!Array}
+     */
+    changeStatuses(change: Change, opt_options?: ChangeStatusesOptions) {
+      const states = [];
+      if (change.status === ChangeStatus.MERGED) {
+        states.push('Merged');
+      } else if (change.status === ChangeStatus.ABANDONED) {
+        states.push('Abandoned');
+      } else if (
+        change.mergeable === false ||
+        (opt_options && opt_options.mergeable === false)
+      ) {
+        // 'mergeable' prop may not always exist (@see Issue 6819)
+        states.push('Merge Conflict');
+      }
+      if (change.work_in_progress) {
+        states.push('WIP');
+      }
+      if (change.is_private) {
+        states.push('Private');
+      }
+
+      // If there are any pre-defined statuses, only return those. Otherwise,
+      // will determine the derived status.
+      if (states.length || !opt_options) {
+        return states;
+      }
+
+      // If no missing requirements, either active or ready to submit.
+      if (change.submittable && opt_options.submitEnabled) {
+        states.push('Ready to submit');
+      } else {
+        // Otherwise it is active.
+        states.push('Active');
+      }
+      return states;
+    },
+
+    /**
+     * @param {!Object} change
+     * @return {string}
+     */
+    changeStatusString(change: Change) {
+      return this.changeStatuses(change).join(', ');
+    },
   },
-
-  listChangesOptionsToHex(...args) {
-    let v = 0;
-    for (let i = 0; i < args.length; i++) {
-      v |= 1 << args[i];
-    }
-    return v.toString(16);
-  },
-
-  /**
-   *  @return {string}
-   */
-  changeBaseURL(project, changeNum, patchNum) {
-    let v = this.getBaseUrl() + '/changes/' +
-       encodeURIComponent(project) + '~' + changeNum;
-    if (patchNum) {
-      v += '/revisions/' + patchNum;
-    }
-    return v;
-  },
-
-  changePath(changeNum) {
-    return this.getBaseUrl() + '/c/' + changeNum;
-  },
-
-  changeIsOpen(change) {
-    return change && change.status === ChangeStatus.NEW;
-  },
-
-  /**
-   * @param {!Object} change
-   * @param {!Object=} opt_options
-   *
-   * @return {!Array}
-   */
-  changeStatuses(change, opt_options) {
-    const states = [];
-    if (change.status === ChangeStatus.MERGED) {
-      states.push('Merged');
-    } else if (change.status === ChangeStatus.ABANDONED) {
-      states.push('Abandoned');
-    } else if (change.mergeable === false ||
-        (opt_options && opt_options.mergeable === false)) {
-      // 'mergeable' prop may not always exist (@see Issue 6819)
-      states.push('Merge Conflict');
-    }
-    if (change.work_in_progress) { states.push('WIP'); }
-    if (change.is_private) { states.push('Private'); }
-
-    // If there are any pre-defined statuses, only return those. Otherwise,
-    // will determine the derived status.
-    if (states.length || !opt_options) { return states; }
-
-    // If no missing requirements, either active or ready to submit.
-    if (change.submittable && opt_options.submitEnabled) {
-      states.push('Ready to submit');
-    } else {
-      // Otherwise it is active.
-      states.push('Active');
-    }
-    return states;
-  },
-
-  /**
-   * @param {!Object} change
-   * @return {string}
-   */
-  changeStatusString(change) {
-    return this.changeStatuses(change).join(', ');
-  },
-},
-BaseUrlBehavior,
+  BaseUrlBehavior,
 ];
 
-// eslint-disable-next-line no-unused-vars
+// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
+// @ts-ignore
 function defineEmptyMixin() {
   // This is a temporary function.
   // Polymer linter doesn't process correctly the following code:
@@ -179,12 +223,27 @@
    * @polymer
    * @mixinFunction
    */
-  const RESTClientMixin = base => // eslint-disable-line no-unused-vars
+  const RESTClientMixin = (
+    // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
+    // @ts-ignore
+    base
+  ) =>
     class extends base {
+      // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
+      // @ts-ignore
       changeStatusString(change) {}
 
+      // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
+      // @ts-ignore
       changeStatuses(change, opt_options) {}
     };
+  // We can't apply @ts-ignore directly to RESTClientMixin - it breaks polylint
+  // tests (polylinter expects that @polymer and @mixinFunction appear right
+  // before the mixin definition). To workaround it and suppress error about
+  // unused variable use a temporary variable.
+  // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
+  // @ts-ignore
+  const tmp = RESTClientMixin;
 }
 
 // TODO(dmfilippov) Remove the following lines with assignments
diff --git a/polygerrit-ui/app/constants/constants.d.ts b/polygerrit-ui/app/constants/constants.d.ts
new file mode 100644
index 0000000..036d6ea
--- /dev/null
+++ b/polygerrit-ui/app/constants/constants.d.ts
@@ -0,0 +1,23 @@
+/**
+ * @license
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export type ChangeStatus = any;
+export namespace ChangeStatus {
+  export const ABANDONED: string;
+  export const MERGED: string;
+  export const NEW: string;
+}
diff --git a/polygerrit-ui/app/constants/constants.js b/polygerrit-ui/app/constants/constants.js
index ff34442..3a4cb5e 100644
--- a/polygerrit-ui/app/constants/constants.js
+++ b/polygerrit-ui/app/constants/constants.js
@@ -15,6 +15,8 @@
  * limitations under the License.
  */
 
+goog.declareModuleId('polygerrit.constants.constants');
+
 /**
  * @enum
  * @desc Tab names for primary tabs on change view page.
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index e0e7fa9..ce925e1 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -15,6 +15,10 @@
  * limitations under the License.
  */
 
+// We need to use goog.declareModuleId internally in google for TS-imports-JS
+// case. To avoid errors when goog is not available, the empty implementation is
+// added.
+window.goog = window.goog || {declareModuleId(name) {}};
 import './gr-app-init.js';
 import './font-roboto-local-loader.js';
 // Sets up global Polymer variable, because plugins requires it.
diff --git a/polygerrit-ui/app/embed/gr-diff.js b/polygerrit-ui/app/embed/gr-diff.js
index 5907842..6050d69 100644
--- a/polygerrit-ui/app/embed/gr-diff.js
+++ b/polygerrit-ui/app/embed/gr-diff.js
@@ -16,6 +16,10 @@
  */
 
 window.Gerrit = window.Gerrit || {};
+// We need to use goog.declareModuleId internally in google for TS-imports-JS
+// case. To avoid errors when goog is not available, the empty implementation is
+// added.
+window.goog = window.goog || {declareModuleId(name) {}};
 // TODO(dmfilippov): remove bundled-polymer.js imports when the following issue
 // https://github.com/Polymer/polymer-resin/issues/9 is resolved.
 // Because gr-diff.js is a shared component, it shouldn' pollute global