Convert files to typescript

The change converts the following files to typescript:

* elements/gr-app-element.ts
* elements/gr-app.ts

Additionally, this change turns off the goog-module-id rule - it is
not required anymore (migration to typescript is almost complete).

Change-Id: I5f0845c03a464322500113b978722a12da7afdee
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
index 606a731..927d32f 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
@@ -32,17 +32,15 @@
 import {
   AccountDetailInfo,
   AccountId,
-  ChangeId,
   ChangeInfo,
   EmailAddress,
-  PatchRange,
   PreferencesInput,
 } from '../../../types/common';
 import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
 import {ChangeListToggleReviewedDetail} from '../gr-change-list-item/gr-change-list-item';
 import {ChangeStarToggleStarDetail} from '../../shared/gr-change-star/gr-change-star';
 import {hasOwnProperty} from '../../../utils/common-util';
-import {DiffViewMode} from '../../../constants/constants';
+import {ChangeListViewState} from '../../../types/types';
 
 const LookupQueryPatterns = {
   CHANGE_ID: /^\s*i?[0-9a-f]{7,40}\s*$/i,
@@ -56,19 +54,6 @@
 
 const LIMIT_OPERATOR_PATTERN = /\blimit:(\d+)/i;
 
-export interface ChangeListViewState {
-  changeNum?: ChangeId;
-  patchRange?: PatchRange;
-  selectedFileIndex?: number;
-  showReplyDialog?: boolean;
-  showDownloadDialog?: boolean;
-  diffMode?: DiffViewMode;
-  numFilesShown?: number;
-  scrollTop?: number;
-  query?: string;
-  offset?: number;
-}
-
 export interface GrChangeListView {
   $: {
     restAPI: RestApiService & Element;
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
index 5a2b43c..3acdaf9 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
@@ -31,7 +31,6 @@
 import {
   KeyboardShortcutMixin,
   Shortcut,
-  CustomKeyboardEvent,
   Modifier,
 } from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
 import {
@@ -56,6 +55,7 @@
   hasAttention,
   isAttentionSetEnabled,
 } from '../../../utils/attention-set-util';
+import {CustomKeyboardEvent} from '../../../types/events';
 
 const NUMBER_FIXED_COLUMNS = 3;
 const CLOSED_STATUS = ['MERGED', 'ABANDONED'];
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts
index 7ca62d9..57d3dd9 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts
@@ -57,13 +57,10 @@
 import {GrOverlay} from '../../shared/gr-overlay/gr-overlay';
 import {ChangeListToggleReviewedDetail} from '../gr-change-list-item/gr-change-list-item';
 import {ChangeStarToggleStarDetail} from '../../shared/gr-change-star/gr-change-star';
+import {DashboardViewState} from '../../../types/types';
 
 const PROJECT_PLACEHOLDER_PATTERN = /\$\{project\}/g;
 
-export interface DashboardViewState {
-  selectedChangeIndex: number;
-}
-
 export interface GrDashboardView {
   $: {
     restAPI: RestApiService & Element;
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
index 0a85954..6491358 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
@@ -51,7 +51,6 @@
 import {
   KeyboardShortcutMixin,
   Shortcut,
-  CustomKeyboardEvent,
 } from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
 import {GrEditConstants} from '../../edit/gr-edit-constants';
 import {GrCountStringFormatter} from '../../shared/gr-count-string-formatter/gr-count-string-formatter';
@@ -110,7 +109,6 @@
 } from '../../../types/common';
 import {GrReplyDialog, FocusTarget} from '../gr-reply-dialog/gr-reply-dialog';
 import {GrIncludedInDialog} from '../gr-included-in-dialog/gr-included-in-dialog';
-import {CommentEventDetail} from '../../shared/gr-comment/gr-comment';
 import {GrDownloadDialog} from '../gr-download-dialog/gr-download-dialog';
 import {GrChangeMetadata} from '../gr-change-metadata/gr-change-metadata';
 import {
@@ -139,7 +137,13 @@
   GrFileList,
   DEFAULT_NUM_FILES_SHOWN,
 } from '../gr-file-list/gr-file-list';
-import {isPolymerSpliceChange} from '../../../types/types';
+import {ChangeViewState, isPolymerSpliceChange} from '../../../types/types';
+import {
+  CustomKeyboardEvent,
+  EditableContentSaveEvent,
+  OpenFixPreviewEvent,
+  SwitchTabEvent,
+} from '../../../types/events';
 
 const CHANGE_ID_ERROR = {
   MISMATCH: 'mismatch',
@@ -188,27 +192,6 @@
 // Making the tab names more unique in case a plugin adds one with same name
 const ROBOT_COMMENTS_LIMIT = 10;
 
-// Type for the custom event to switch tab.
-interface SwitchTabEventDetail {
-  // name of the tab to set as active, from custom event
-  tab?: string;
-  // index of tab to set as active, from paper-tabs event
-  value?: number;
-  // scroll into the tab afterwards, from custom event
-  scrollIntoView?: boolean;
-}
-
-export interface ChangeViewState {
-  diffMode?: DiffViewMode;
-  scrollTop?: number;
-  showDownloadDialog?: boolean;
-  showReplyDialog?: boolean;
-  changeNum?: NumericChangeId;
-  numFilesShown?: number;
-  patchRange?: PatchRange;
-  diffViewMode?: boolean;
-}
-
 export interface GrChangeView {
   $: {
     restAPI: RestApiService & Element;
@@ -271,7 +254,7 @@
   params?: AppElementChangeViewParams;
 
   @property({type: Object, notify: true, observer: '_viewStateChanged'})
-  viewState: ChangeViewState = {};
+  viewState: Partial<ChangeViewState> = {};
 
   @property({type: String})
   backPage?: string;
@@ -634,30 +617,27 @@
     );
     this.addEventListener('change-message-deleted', () => this._reload());
     this.addEventListener('editable-content-save', e =>
-      this._handleCommitMessageSave(e as CustomEvent<{content: string}>)
+      this._handleCommitMessageSave(e)
     );
     this.addEventListener('editable-content-cancel', () =>
       this._handleCommitMessageCancel()
     );
-    this.addEventListener('open-fix-preview', e =>
-      this._onOpenFixPreview(e as CustomEvent<CommentEventDetail>)
-    );
+    this.addEventListener('open-fix-preview', e => this._onOpenFixPreview(e));
     this.addEventListener('close-fix-preview', () => this._onCloseFixPreview());
     this.listen(window, 'scroll', '_handleScroll');
     this.listen(document, 'visibilitychange', '_handleVisibilityChange');
 
     this.addEventListener('show-primary-tab', e =>
-      this._setActivePrimaryTab(e as CustomEvent<SwitchTabEventDetail>)
+      this._setActivePrimaryTab(e)
     );
     this.addEventListener('show-secondary-tab', e =>
-      this._setActiveSecondaryTab(e as CustomEvent<SwitchTabEventDetail>)
+      this._setActiveSecondaryTab(e)
     );
     this.addEventListener('reload', e => {
       e.stopPropagation();
-      const evt = e as CustomEvent<{clearPatchset: boolean}>;
       this._reload(
         /* isLocationChange= */ false,
-        /* clearPatchset= */ evt.detail && evt.detail.clearPatchset
+        /* clearPatchset= */ e.detail && e.detail.clearPatchset
       );
     });
   }
@@ -703,7 +683,7 @@
       });
   }
 
-  _onOpenFixPreview(e: CustomEvent<CommentEventDetail>) {
+  _onOpenFixPreview(e: OpenFixPreviewEvent) {
     this.$.applyFixDialog.open(e);
   }
 
@@ -775,7 +755,7 @@
   /**
    * Changes active primary tab.
    */
-  _setActivePrimaryTab(e: CustomEvent<SwitchTabEventDetail>) {
+  _setActivePrimaryTab(e: SwitchTabEvent) {
     const primaryTabs = this.shadowRoot!.querySelector(
       '#primaryTabs'
     ) as PaperTabsElement;
@@ -808,7 +788,7 @@
   /**
    * Changes active secondary tab.
    */
-  _setActiveSecondaryTab(e: CustomEvent<SwitchTabEventDetail>) {
+  _setActiveSecondaryTab(e: SwitchTabEvent) {
     const secondaryTabs = this.shadowRoot!.querySelector(
       '#secondaryTabs'
     ) as PaperTabsElement;
@@ -827,7 +807,7 @@
     this.$.commitMessageEditor.focusTextarea();
   }
 
-  _handleCommitMessageSave(e: CustomEvent<{content: string}>) {
+  _handleCommitMessageSave(e: EditableContentSaveEvent) {
     if (!this._change) throw new Error('missing required change property');
     if (!this._changeNum)
       throw new Error('missing required changeNum property');
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
index 49f6992..3243af0 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
@@ -34,7 +34,6 @@
 import {htmlTemplate} from './gr-file-list_html';
 import {asyncForeach} from '../../../utils/async-util';
 import {
-  CustomKeyboardEvent,
   KeyboardShortcutMixin,
   Modifier,
   Shortcut,
@@ -78,6 +77,7 @@
 import {UIDraft} from '../../../utils/comment-util';
 import {ParsedChangeInfo} from '../../shared/gr-rest-api-interface/gr-reviewer-updates-parser';
 import {PatchSetFile} from '../../../types/types';
+import {CustomKeyboardEvent} from '../../../types/events';
 
 export const DEFAULT_NUM_FILES_SHOWN = 200;
 
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.ts b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.ts
index bd3c052..7a56d1c 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.ts
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.ts
@@ -38,6 +38,7 @@
 import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
 import {FetchRequest} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
 import {ErrorType, FixIronA11yAnnouncer} from '../../../types/types';
+import {AccountId} from '../../../types/common';
 
 const HIDE_ALERT_TIMEOUT_MS = 5000;
 const CHECK_SIGN_IN_INTERVAL_MS = 60 * 1000;
@@ -86,7 +87,7 @@
    * not set, then there was no account at launch.
    */
   @property({type: Number})
-  knownAccountId?: number;
+  knownAccountId?: AccountId | null;
 
   @property({type: Object})
   _alertElement: GrAlert | null = null;
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
index 21ff4dc..df3415d 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
@@ -66,6 +66,7 @@
   AppElementParams,
   AppElementAgreementParam,
 } from '../../gr-app-types';
+import {LocationChangeEventDetail} from '../../../types/events';
 
 const RoutePattern = {
   ROOT: '/',
@@ -854,12 +855,13 @@
       // Fire asynchronously so that the URL is changed by the time the event
       // is processed.
       this.async(() => {
+        const detail: LocationChangeEventDetail = {
+          hash: window.location.hash,
+          pathname: window.location.pathname,
+        };
         this.dispatchEvent(
           new CustomEvent('location-change', {
-            detail: {
-              hash: window.location.hash,
-              pathname: window.location.pathname,
-            },
+            detail,
             composed: true,
             bubbles: true,
           })
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
index 180e4a7..68e045f 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
@@ -23,7 +23,6 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element';
 import {htmlTemplate} from './gr-search-bar_html';
 import {
-  CustomKeyboardEvent,
   KeyboardShortcutMixin,
   Shortcut,
 } from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
@@ -36,6 +35,7 @@
   GrAutocomplete,
 } from '../../shared/gr-autocomplete/gr-autocomplete';
 import {getDocsBaseUrl} from '../../../utils/url-util';
+import {CustomKeyboardEvent} from '../../../types/events';
 
 // Possible static search options for auto complete, without negations.
 const SEARCH_OPERATORS = [
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
index 2236db2..0e73516 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
@@ -38,9 +38,9 @@
   RobotId,
 } from '../../../types/common';
 import {GrOverlay} from '../../shared/gr-overlay/gr-overlay';
-import {CommentEventDetail} from '../../shared/gr-comment/gr-comment';
 import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
 import {isRobot} from '../../../utils/comment-util';
+import {OpenFixPreviewEvent} from '../../../types/events';
 
 export interface GrApplyFixDialog {
   $: {
@@ -110,7 +110,7 @@
    * @return Promise that resolves either when all
    * preview diffs are fetched or no fix suggestions in custom event detail.
    */
-  open(e: CustomEvent<CommentEventDetail>) {
+  open(e: OpenFixPreviewEvent) {
     const detail = e.detail;
     const comment = detail.comment;
     if (!detail.patchNum || !comment || !isRobot(comment)) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
index 484b55d..848443a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
@@ -37,7 +37,6 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element';
 import {htmlTemplate} from './gr-diff-view_html';
 import {
-  CustomKeyboardEvent,
   KeyboardShortcutMixin,
   Shortcut,
 } from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
@@ -98,10 +97,10 @@
 import {hasOwnProperty} from '../../../utils/common-util';
 import {GrApplyFixDialog} from '../gr-apply-fix-dialog/gr-apply-fix-dialog';
 import {LineOfInterest} from '../gr-diff/gr-diff';
-import {CommentEventDetail} from '../../shared/gr-comment/gr-comment';
 import {RevisionInfo as RevisionInfoObj} from '../../shared/revision-info/revision-info';
 import {CommentMap} from '../../../utils/comment-util';
 import {AppElementParams} from '../../gr-app-types';
+import {CustomKeyboardEvent, OpenFixPreviewEvent} from '../../../types/events';
 
 const ERR_REVIEW_STATUS = 'Couldn’t change file review status.';
 const MSG_LOADING_BLAME = 'Loading blame...';
@@ -343,9 +342,7 @@
       this._loggedIn = loggedIn;
     });
 
-    this.addEventListener('open-fix-preview', e =>
-      this._onOpenFixPreview(e as CustomEvent<CommentEventDetail>)
-    );
+    this.addEventListener('open-fix-preview', e => this._onOpenFixPreview(e));
     this.$.cursor.push('diffs', this.$.diffHost);
     this._onRenderHandler = (_: Event) => {
       this.$.cursor.reInitCursor();
@@ -539,7 +536,7 @@
     this.$.cursor.moveToVisibleArea();
   }
 
-  _onOpenFixPreview(e: CustomEvent<CommentEventDetail>) {
+  _onOpenFixPreview(e: OpenFixPreviewEvent) {
     this.$.applyFixDialog.open(e);
   }
 
diff --git a/polygerrit-ui/app/elements/gr-app-element.ts b/polygerrit-ui/app/elements/gr-app-element.ts
index 93b051c..6452239 100644
--- a/polygerrit-ui/app/elements/gr-app-element.ts
+++ b/polygerrit-ui/app/elements/gr-app-element.ts
@@ -14,147 +14,185 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../styles/shared-styles.js';
-import '../styles/themes/app-theme.js';
-import {applyTheme as applyDarkTheme} from '../styles/themes/dark-theme.js';
-import './admin/gr-admin-view/gr-admin-view.js';
-import './documentation/gr-documentation-search/gr-documentation-search.js';
-import './change-list/gr-change-list-view/gr-change-list-view.js';
-import './change-list/gr-dashboard-view/gr-dashboard-view.js';
-import './change/gr-change-view/gr-change-view.js';
-import './core/gr-error-manager/gr-error-manager.js';
-import './core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js';
-import './core/gr-main-header/gr-main-header.js';
-import './core/gr-router/gr-router.js';
-import './core/gr-smart-search/gr-smart-search.js';
-import './diff/gr-diff-view/gr-diff-view.js';
-import './edit/gr-editor-view/gr-editor-view.js';
-import './plugins/gr-endpoint-decorator/gr-endpoint-decorator.js';
-import './plugins/gr-endpoint-param/gr-endpoint-param.js';
-import './plugins/gr-endpoint-slot/gr-endpoint-slot.js';
-import './plugins/gr-external-style/gr-external-style.js';
-import './plugins/gr-plugin-host/gr-plugin-host.js';
-import './settings/gr-cla-view/gr-cla-view.js';
-import './settings/gr-registration-dialog/gr-registration-dialog.js';
-import './settings/gr-settings-view/gr-settings-view.js';
-import './shared/gr-lib-loader/gr-lib-loader.js';
-import './shared/gr-rest-api-interface/gr-rest-api-interface.js';
-import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
-import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
-import {PolymerElement} from '@polymer/polymer/polymer-element.js';
-import {htmlTemplate} from './gr-app-element_html.js';
-import {getBaseUrl} from '../utils/url-util.js';
+import '../styles/shared-styles';
+import '../styles/themes/app-theme';
+import {applyTheme as applyDarkTheme} from '../styles/themes/dark-theme';
+import './admin/gr-admin-view/gr-admin-view';
+import './documentation/gr-documentation-search/gr-documentation-search';
+import './change-list/gr-change-list-view/gr-change-list-view';
+import './change-list/gr-dashboard-view/gr-dashboard-view';
+import './change/gr-change-view/gr-change-view';
+import './core/gr-error-manager/gr-error-manager';
+import './core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog';
+import './core/gr-main-header/gr-main-header';
+import './core/gr-router/gr-router';
+import './core/gr-smart-search/gr-smart-search';
+import './diff/gr-diff-view/gr-diff-view';
+import './edit/gr-editor-view/gr-editor-view';
+import './plugins/gr-endpoint-decorator/gr-endpoint-decorator';
+import './plugins/gr-endpoint-param/gr-endpoint-param';
+import './plugins/gr-endpoint-slot/gr-endpoint-slot';
+import './plugins/gr-external-style/gr-external-style';
+import './plugins/gr-plugin-host/gr-plugin-host';
+import './settings/gr-cla-view/gr-cla-view';
+import './settings/gr-registration-dialog/gr-registration-dialog';
+import './settings/gr-settings-view/gr-settings-view';
+import './shared/gr-lib-loader/gr-lib-loader';
+import './shared/gr-rest-api-interface/gr-rest-api-interface';
+import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners';
+import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
+import {PolymerElement} from '@polymer/polymer/polymer-element';
+import {htmlTemplate} from './gr-app-element_html';
+import {getBaseUrl} from '../utils/url-util';
 import {
   KeyboardShortcutMixin,
   Shortcut,
   SPECIAL_SHORTCUT,
-} from '../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js';
-import {GerritNav} from './core/gr-navigation/gr-navigation.js';
-import {appContext} from '../services/app-context.js';
+} from '../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
+import {GerritNav, GerritView} from './core/gr-navigation/gr-navigation';
+import {appContext} from '../services/app-context';
 import {flush} from '@polymer/polymer/lib/utils/flush';
+import {customElement, observe, property} from '@polymer/decorators';
+import {RestApiService} from '../services/services/gr-rest-api/gr-rest-api';
+import {GrRouter} from './core/gr-router/gr-router';
+import {
+  AccountDetailInfo,
+  ElementPropertyDeepChange,
+  ServerInfo,
+} from '../types/common';
+import {GrErrorManager} from './core/gr-error-manager/gr-error-manager';
+import {GrOverlay} from './shared/gr-overlay/gr-overlay';
+import {GrRegistrationDialog} from './settings/gr-registration-dialog/gr-registration-dialog';
+import {
+  AppElementJustRegisteredParams,
+  AppElementParams,
+  isAppElementJustRegisteredParams,
+} from './gr-app-types';
+import {GrMainHeader} from './core/gr-main-header/gr-main-header';
+import {GrSettingsView} from './settings/gr-settings-view/gr-settings-view';
+import {
+  CustomKeyboardEvent,
+  LocationChangeEvent,
+  PageErrorEventDetail,
+  RpcLogEvent,
+  ShortcutTriggeredEvent,
+  TitleChangeEventDetail,
+} from '../types/events';
+import {ViewState} from '../types/types';
 
-/**
- * @extends PolymerElement
- */
-class GrAppElement extends KeyboardShortcutMixin(
-    GestureEventListeners(
-        LegacyElementMixin(PolymerElement))) {
-  static get template() { return htmlTemplate; }
+interface ErrorInfo {
+  text: string;
+  emoji?: string;
+  moreInfo?: string;
+}
 
-  static get is() { return 'gr-app-element'; }
+export interface GrAppElement {
+  $: {
+    restAPI: RestApiService & Element;
+    router: GrRouter;
+    errorManager: GrErrorManager;
+    errorView: HTMLDivElement;
+    mainHeader: GrMainHeader;
+  };
+}
+
+// TODO(TS): implement AppElement interface from gr-app-types.ts
+@customElement('gr-app-element')
+export class GrAppElement extends KeyboardShortcutMixin(
+  GestureEventListeners(LegacyElementMixin(PolymerElement))
+) {
+  static get template() {
+    return htmlTemplate;
+  }
+
   /**
    * Fired when the URL location changes.
    *
    * @event location-change
    */
 
-  static get properties() {
-    return {
-    /**
-     * @type {{ query: string, view: string, screen: string }}
-     */
-      params: Object,
-      keyEventTarget: {
-        type: Object,
-        value() { return document.body; },
-      },
+  @property({type: Object})
+  params?: AppElementParams;
 
-      _account: {
-        type: Object,
-        observer: '_accountChanged',
-      },
+  @property({type: Object})
+  keyEventTarget = document.body;
 
-      /**
-       * The last time the g key was pressed in milliseconds (or a keydown event
-       * was handled if the key is held down).
-       *
-       * @type {number|null}
-       */
-      _lastGKeyPressTimestamp: {
-        type: Number,
-        value: null,
-      },
+  @property({type: Object, observer: '_accountChanged'})
+  _account?: AccountDetailInfo;
 
-      /**
-       * @type {{ plugin: Object }}
-       */
-      _serverConfig: Object,
-      _version: String,
-      _showChangeListView: Boolean,
-      _showDashboardView: Boolean,
-      _showChangeView: Boolean,
-      _showDiffView: Boolean,
-      _showSettingsView: Boolean,
-      _showAdminView: Boolean,
-      _showCLAView: Boolean,
-      _showEditorView: Boolean,
-      _showPluginScreen: Boolean,
-      _showDocumentationSearch: Boolean,
-      /** @type {?} */
-      _viewState: Object,
-      /** @type {?} */
-      _lastError: Object,
-      _lastSearchPage: String,
-      _path: String,
-      _pluginScreenName: {
-        type: String,
-        computed: '_computePluginScreenName(params)',
-      },
-      _settingsUrl: String,
-      _feedbackUrl: String,
-      // Used to allow searching on mobile
-      mobileSearch: {
-        type: Boolean,
-        value: false,
-      },
+  @property({type: Number})
+  _lastGKeyPressTimestamp: number | null = null;
 
-      /**
-       * Other elements in app must open this URL when
-       * user login is required.
-       */
-      _loginUrl: {
-        type: String,
-        value: '/login',
-      },
+  @property({type: Object})
+  _serverConfig?: ServerInfo;
 
-      loadRegistrationDialog: {
-        type: Boolean,
-        value: false,
-      },
+  @property({type: String})
+  _version?: string;
 
-      loadKeyboardShortcutsDialog: {
-        type: Boolean,
-        value: false,
-      },
-    };
-  }
+  @property({type: Boolean})
+  _showChangeListView?: boolean;
 
-  static get observers() {
-    return [
-      '_viewChanged(params.view)',
-      '_paramsChanged(params.*)',
-    ];
-  }
+  @property({type: Boolean})
+  _showDashboardView?: boolean;
+
+  @property({type: Boolean})
+  _showChangeView?: boolean;
+
+  @property({type: Boolean})
+  _showDiffView?: boolean;
+
+  @property({type: Boolean})
+  _showSettingsView?: boolean;
+
+  @property({type: Boolean})
+  _showAdminView?: boolean;
+
+  @property({type: Boolean})
+  _showCLAView?: boolean;
+
+  @property({type: Boolean})
+  _showEditorView?: boolean;
+
+  @property({type: Boolean})
+  _showPluginScreen?: boolean;
+
+  @property({type: Boolean})
+  _showDocumentationSearch?: boolean;
+
+  @property({type: Object})
+  _viewState?: ViewState;
+
+  @property({type: Object})
+  _lastError?: ErrorInfo;
+
+  @property({type: String})
+  _lastSearchPage?: string;
+
+  @property({type: String})
+  _path?: string;
+
+  @property({type: String, computed: '_computePluginScreenName(params)'})
+  _pluginScreenName?: string;
+
+  @property({type: String})
+  _settingsUrl?: string;
+
+  @property({type: String})
+  _feedbackUrl?: string;
+
+  @property({type: Boolean})
+  mobileSearch = false;
+
+  @property({type: String})
+  _loginUrl = '/login';
+
+  @property({type: Boolean})
+  loadRegistrationDialog = false;
+
+  @property({type: Boolean})
+  loadKeyboardShortcutsDialog = false;
+
+  private reporting = appContext.reportingService;
 
   keyboardShortcuts() {
     return {
@@ -167,29 +205,23 @@
     };
   }
 
-  constructor() {
-    super();
-    this.reporting = appContext.reportingService;
-  }
-
   /** @override */
   created() {
     super.created();
     this._bindKeyboardShortcuts();
-    this.addEventListener('page-error',
-        e => this._handlePageError(e));
-    this.addEventListener('title-change',
-        e => this._handleTitleChange(e));
-    this.addEventListener('location-change',
-        e => this._handleLocationChange(e));
-    this.addEventListener('rpc-log',
-        e => this._handleRpcLog(e));
-    this.addEventListener('shortcut-triggered',
-        e => this._handleShortcutTriggered(e));
+    this.addEventListener('page-error', e => this._handlePageError(e));
+    this.addEventListener('title-change', e => this._handleTitleChange(e));
+    this.addEventListener('location-change', e =>
+      this._handleLocationChange(e)
+    );
+    this.addEventListener('rpc-log', e => this._handleRpcLog(e));
+    this.addEventListener('shortcut-triggered', e =>
+      this._handleShortcutTriggered(e)
+    );
     // Ideally individual views should handle this event and respond with a soft
     // reload. This is a catch-all for all views that cannot or have not
     // implemented that.
-    this.addEventListener('reload', e => window.location.reload());
+    this.addEventListener('reload', () => window.location.reload());
   }
 
   /** @override */
@@ -247,150 +279,145 @@
   }
 
   _bindKeyboardShortcuts() {
-    this.bindShortcut(Shortcut.SEND_REPLY,
-        SPECIAL_SHORTCUT.DOC_ONLY, 'ctrl+enter', 'meta+enter');
-    this.bindShortcut(Shortcut.EMOJI_DROPDOWN,
-        SPECIAL_SHORTCUT.DOC_ONLY, ':');
+    this.bindShortcut(
+      Shortcut.SEND_REPLY,
+      SPECIAL_SHORTCUT.DOC_ONLY,
+      'ctrl+enter',
+      'meta+enter'
+    );
+    this.bindShortcut(Shortcut.EMOJI_DROPDOWN, SPECIAL_SHORTCUT.DOC_ONLY, ':');
 
+    this.bindShortcut(Shortcut.OPEN_SHORTCUT_HELP_DIALOG, '?');
     this.bindShortcut(
-        Shortcut.OPEN_SHORTCUT_HELP_DIALOG, '?');
+      Shortcut.GO_TO_USER_DASHBOARD,
+      SPECIAL_SHORTCUT.GO_KEY,
+      'i'
+    );
     this.bindShortcut(
-        Shortcut.GO_TO_USER_DASHBOARD, SPECIAL_SHORTCUT.GO_KEY, 'i');
+      Shortcut.GO_TO_OPENED_CHANGES,
+      SPECIAL_SHORTCUT.GO_KEY,
+      'o'
+    );
     this.bindShortcut(
-        Shortcut.GO_TO_OPENED_CHANGES, SPECIAL_SHORTCUT.GO_KEY, 'o');
+      Shortcut.GO_TO_MERGED_CHANGES,
+      SPECIAL_SHORTCUT.GO_KEY,
+      'm'
+    );
     this.bindShortcut(
-        Shortcut.GO_TO_MERGED_CHANGES, SPECIAL_SHORTCUT.GO_KEY, 'm');
+      Shortcut.GO_TO_ABANDONED_CHANGES,
+      SPECIAL_SHORTCUT.GO_KEY,
+      'a'
+    );
     this.bindShortcut(
-        Shortcut.GO_TO_ABANDONED_CHANGES, SPECIAL_SHORTCUT.GO_KEY, 'a');
-    this.bindShortcut(
-        Shortcut.GO_TO_WATCHED_CHANGES, SPECIAL_SHORTCUT.GO_KEY, 'w');
+      Shortcut.GO_TO_WATCHED_CHANGES,
+      SPECIAL_SHORTCUT.GO_KEY,
+      'w'
+    );
 
-    this.bindShortcut(
-        Shortcut.CURSOR_NEXT_CHANGE, 'j');
-    this.bindShortcut(
-        Shortcut.CURSOR_PREV_CHANGE, 'k');
-    this.bindShortcut(
-        Shortcut.OPEN_CHANGE, 'o');
-    this.bindShortcut(
-        Shortcut.NEXT_PAGE, 'n', ']');
-    this.bindShortcut(
-        Shortcut.PREV_PAGE, 'p', '[');
-    this.bindShortcut(
-        Shortcut.TOGGLE_CHANGE_REVIEWED, 'r:keyup');
-    this.bindShortcut(
-        Shortcut.TOGGLE_CHANGE_STAR, 's:keydown');
-    this.bindShortcut(
-        Shortcut.REFRESH_CHANGE_LIST, 'shift+r:keyup');
-    this.bindShortcut(
-        Shortcut.EDIT_TOPIC, 't');
+    this.bindShortcut(Shortcut.CURSOR_NEXT_CHANGE, 'j');
+    this.bindShortcut(Shortcut.CURSOR_PREV_CHANGE, 'k');
+    this.bindShortcut(Shortcut.OPEN_CHANGE, 'o');
+    this.bindShortcut(Shortcut.NEXT_PAGE, 'n', ']');
+    this.bindShortcut(Shortcut.PREV_PAGE, 'p', '[');
+    this.bindShortcut(Shortcut.TOGGLE_CHANGE_REVIEWED, 'r:keyup');
+    this.bindShortcut(Shortcut.TOGGLE_CHANGE_STAR, 's:keydown');
+    this.bindShortcut(Shortcut.REFRESH_CHANGE_LIST, 'shift+r:keyup');
+    this.bindShortcut(Shortcut.EDIT_TOPIC, 't');
 
+    this.bindShortcut(Shortcut.OPEN_REPLY_DIALOG, 'a:keyup');
+    this.bindShortcut(Shortcut.OPEN_DOWNLOAD_DIALOG, 'd:keyup');
+    this.bindShortcut(Shortcut.EXPAND_ALL_MESSAGES, 'x');
+    this.bindShortcut(Shortcut.COLLAPSE_ALL_MESSAGES, 'z');
+    this.bindShortcut(Shortcut.REFRESH_CHANGE, 'shift+r:keyup');
+    this.bindShortcut(Shortcut.UP_TO_DASHBOARD, 'u');
+    this.bindShortcut(Shortcut.UP_TO_CHANGE, 'u');
+    this.bindShortcut(Shortcut.TOGGLE_DIFF_MODE, 'm:keyup');
     this.bindShortcut(
-        Shortcut.OPEN_REPLY_DIALOG, 'a:keyup');
-    this.bindShortcut(
-        Shortcut.OPEN_DOWNLOAD_DIALOG, 'd:keyup');
-    this.bindShortcut(
-        Shortcut.EXPAND_ALL_MESSAGES, 'x');
-    this.bindShortcut(
-        Shortcut.COLLAPSE_ALL_MESSAGES, 'z');
-    this.bindShortcut(
-        Shortcut.REFRESH_CHANGE, 'shift+r:keyup');
-    this.bindShortcut(
-        Shortcut.UP_TO_DASHBOARD, 'u');
-    this.bindShortcut(
-        Shortcut.UP_TO_CHANGE, 'u');
-    this.bindShortcut(
-        Shortcut.TOGGLE_DIFF_MODE, 'm:keyup');
-    this.bindShortcut(
-        Shortcut.DIFF_AGAINST_BASE, SPECIAL_SHORTCUT.V_KEY, 'down', 's');
+      Shortcut.DIFF_AGAINST_BASE,
+      SPECIAL_SHORTCUT.V_KEY,
+      'down',
+      's'
+    );
     // this keyboard shortcut is used in toast _displayDiffAgainstLatestToast
     // in gr-diff-view. Any updates here should be reflected there
     this.bindShortcut(
-        Shortcut.DIFF_AGAINST_LATEST, SPECIAL_SHORTCUT.V_KEY, 'up', 'w');
+      Shortcut.DIFF_AGAINST_LATEST,
+      SPECIAL_SHORTCUT.V_KEY,
+      'up',
+      'w'
+    );
     // this keyboard shortcut is used in toast _displayDiffBaseAgainstLeftToast
     // in gr-diff-view. Any updates here should be reflected there
     this.bindShortcut(
-        Shortcut.DIFF_BASE_AGAINST_LEFT,
-        SPECIAL_SHORTCUT.V_KEY, 'left', 'a');
+      Shortcut.DIFF_BASE_AGAINST_LEFT,
+      SPECIAL_SHORTCUT.V_KEY,
+      'left',
+      'a'
+    );
     this.bindShortcut(
-        Shortcut.DIFF_RIGHT_AGAINST_LATEST,
-        SPECIAL_SHORTCUT.V_KEY, 'right', 'd');
+      Shortcut.DIFF_RIGHT_AGAINST_LATEST,
+      SPECIAL_SHORTCUT.V_KEY,
+      'right',
+      'd'
+    );
     this.bindShortcut(
-        Shortcut.DIFF_BASE_AGAINST_LATEST, SPECIAL_SHORTCUT.V_KEY, 'b');
+      Shortcut.DIFF_BASE_AGAINST_LATEST,
+      SPECIAL_SHORTCUT.V_KEY,
+      'b'
+    );
 
-    this.bindShortcut(
-        Shortcut.NEXT_LINE, 'j', 'down');
-    this.bindShortcut(
-        Shortcut.PREV_LINE, 'k', 'up');
+    this.bindShortcut(Shortcut.NEXT_LINE, 'j', 'down');
+    this.bindShortcut(Shortcut.PREV_LINE, 'k', 'up');
     if (this._isCursorManagerSupportMoveToVisibleLine()) {
-      this.bindShortcut(
-          Shortcut.VISIBLE_LINE, '.');
+      this.bindShortcut(Shortcut.VISIBLE_LINE, '.');
     }
+    this.bindShortcut(Shortcut.NEXT_CHUNK, 'n');
+    this.bindShortcut(Shortcut.PREV_CHUNK, 'p');
+    this.bindShortcut(Shortcut.EXPAND_ALL_DIFF_CONTEXT, 'shift+x');
+    this.bindShortcut(Shortcut.NEXT_COMMENT_THREAD, 'shift+n');
+    this.bindShortcut(Shortcut.PREV_COMMENT_THREAD, 'shift+p');
     this.bindShortcut(
-        Shortcut.NEXT_CHUNK, 'n');
+      Shortcut.EXPAND_ALL_COMMENT_THREADS,
+      SPECIAL_SHORTCUT.DOC_ONLY,
+      'e'
+    );
     this.bindShortcut(
-        Shortcut.PREV_CHUNK, 'p');
+      Shortcut.COLLAPSE_ALL_COMMENT_THREADS,
+      SPECIAL_SHORTCUT.DOC_ONLY,
+      'shift+e'
+    );
+    this.bindShortcut(Shortcut.LEFT_PANE, 'shift+left');
+    this.bindShortcut(Shortcut.RIGHT_PANE, 'shift+right');
+    this.bindShortcut(Shortcut.TOGGLE_LEFT_PANE, 'shift+a');
+    this.bindShortcut(Shortcut.NEW_COMMENT, 'c');
     this.bindShortcut(
-        Shortcut.EXPAND_ALL_DIFF_CONTEXT, 'shift+x');
-    this.bindShortcut(
-        Shortcut.NEXT_COMMENT_THREAD, 'shift+n');
-    this.bindShortcut(
-        Shortcut.PREV_COMMENT_THREAD, 'shift+p');
-    this.bindShortcut(
-        Shortcut.EXPAND_ALL_COMMENT_THREADS,
-        SPECIAL_SHORTCUT.DOC_ONLY, 'e');
-    this.bindShortcut(
-        Shortcut.COLLAPSE_ALL_COMMENT_THREADS,
-        SPECIAL_SHORTCUT.DOC_ONLY, 'shift+e');
-    this.bindShortcut(
-        Shortcut.LEFT_PANE, 'shift+left');
-    this.bindShortcut(
-        Shortcut.RIGHT_PANE, 'shift+right');
-    this.bindShortcut(
-        Shortcut.TOGGLE_LEFT_PANE, 'shift+a');
-    this.bindShortcut(
-        Shortcut.NEW_COMMENT, 'c');
-    this.bindShortcut(
-        Shortcut.SAVE_COMMENT,
-        'ctrl+enter', 'meta+enter', 'ctrl+s', 'meta+s');
-    this.bindShortcut(
-        Shortcut.OPEN_DIFF_PREFS, ',');
-    this.bindShortcut(
-        Shortcut.TOGGLE_DIFF_REVIEWED, 'r:keyup');
+      Shortcut.SAVE_COMMENT,
+      'ctrl+enter',
+      'meta+enter',
+      'ctrl+s',
+      'meta+s'
+    );
+    this.bindShortcut(Shortcut.OPEN_DIFF_PREFS, ',');
+    this.bindShortcut(Shortcut.TOGGLE_DIFF_REVIEWED, 'r:keyup');
 
-    this.bindShortcut(
-        Shortcut.NEXT_FILE, ']');
-    this.bindShortcut(
-        Shortcut.PREV_FILE, '[');
-    this.bindShortcut(
-        Shortcut.NEXT_FILE_WITH_COMMENTS, 'shift+j');
-    this.bindShortcut(
-        Shortcut.PREV_FILE_WITH_COMMENTS, 'shift+k');
-    this.bindShortcut(
-        Shortcut.CURSOR_NEXT_FILE, 'j', 'down');
-    this.bindShortcut(
-        Shortcut.CURSOR_PREV_FILE, 'k', 'up');
-    this.bindShortcut(
-        Shortcut.OPEN_FILE, 'o', 'enter');
-    this.bindShortcut(
-        Shortcut.TOGGLE_FILE_REVIEWED, 'r:keyup');
-    this.bindShortcut(
-        Shortcut.NEXT_UNREVIEWED_FILE, 'shift+m');
-    this.bindShortcut(
-        Shortcut.TOGGLE_ALL_INLINE_DIFFS, 'shift+i:keyup');
-    this.bindShortcut(
-        Shortcut.TOGGLE_INLINE_DIFF, 'i:keyup');
-    this.bindShortcut(
-        Shortcut.TOGGLE_BLAME, 'b:keyup');
-    this.bindShortcut(
-        Shortcut.TOGGLE_HIDE_ALL_COMMENT_THREADS, 'h');
+    this.bindShortcut(Shortcut.NEXT_FILE, ']');
+    this.bindShortcut(Shortcut.PREV_FILE, '[');
+    this.bindShortcut(Shortcut.NEXT_FILE_WITH_COMMENTS, 'shift+j');
+    this.bindShortcut(Shortcut.PREV_FILE_WITH_COMMENTS, 'shift+k');
+    this.bindShortcut(Shortcut.CURSOR_NEXT_FILE, 'j', 'down');
+    this.bindShortcut(Shortcut.CURSOR_PREV_FILE, 'k', 'up');
+    this.bindShortcut(Shortcut.OPEN_FILE, 'o', 'enter');
+    this.bindShortcut(Shortcut.TOGGLE_FILE_REVIEWED, 'r:keyup');
+    this.bindShortcut(Shortcut.NEXT_UNREVIEWED_FILE, 'shift+m');
+    this.bindShortcut(Shortcut.TOGGLE_ALL_INLINE_DIFFS, 'shift+i:keyup');
+    this.bindShortcut(Shortcut.TOGGLE_INLINE_DIFF, 'i:keyup');
+    this.bindShortcut(Shortcut.TOGGLE_BLAME, 'b:keyup');
+    this.bindShortcut(Shortcut.TOGGLE_HIDE_ALL_COMMENT_THREADS, 'h');
 
-    this.bindShortcut(
-        Shortcut.OPEN_FIRST_FILE, ']');
-    this.bindShortcut(
-        Shortcut.OPEN_LAST_FILE, '[');
+    this.bindShortcut(Shortcut.OPEN_FIRST_FILE, ']');
+    this.bindShortcut(Shortcut.OPEN_LAST_FILE, '[');
 
-    this.bindShortcut(
-        Shortcut.SEARCH, '/');
+    this.bindShortcut(Shortcut.SEARCH, '/');
   }
 
   _isCursorManagerSupportMoveToVisibleLine() {
@@ -401,30 +428,35 @@
     return 'IntersectionObserver' in window;
   }
 
-  _accountChanged(account) {
-    if (!account) { return; }
+  _accountChanged(account?: AccountDetailInfo) {
+    if (!account) return;
 
     // Preferences are cached when a user is logged in; warm them.
     this.$.restAPI.getPreferences();
     this.$.restAPI.getDiffPreferences();
     this.$.restAPI.getEditPreferences();
     this.$.errorManager.knownAccountId =
-        this._account && this._account._account_id || null;
+      (this._account && this._account._account_id) || null;
   }
 
-  _viewChanged(view) {
+  @observe('params.view')
+  _viewChanged(view?: GerritView) {
     this.$.errorView.classList.remove('show');
-    this.set('_showChangeListView', view === GerritNav.View.SEARCH);
-    this.set('_showDashboardView', view === GerritNav.View.DASHBOARD);
-    this.set('_showChangeView', view === GerritNav.View.CHANGE);
-    this.set('_showDiffView', view === GerritNav.View.DIFF);
-    this.set('_showSettingsView', view === GerritNav.View.SETTINGS);
+    this.set('_showChangeListView', view === GerritView.SEARCH);
+    this.set('_showDashboardView', view === GerritView.DASHBOARD);
+    this.set('_showChangeView', view === GerritView.CHANGE);
+    this.set('_showDiffView', view === GerritView.DIFF);
+    this.set('_showSettingsView', view === GerritView.SETTINGS);
     // _showAdminView must be in sync with the gr-admin-view AdminViewParams type
-    this.set('_showAdminView', view === GerritNav.View.ADMIN ||
-        view === GerritNav.View.GROUP || view === GerritNav.View.REPO);
-    this.set('_showCLAView', view === GerritNav.View.AGREEMENTS);
-    this.set('_showEditorView', view === GerritNav.View.EDIT);
-    const isPluginScreen = view === GerritNav.View.PLUGIN_SCREEN;
+    this.set(
+      '_showAdminView',
+      view === GerritView.ADMIN ||
+        view === GerritView.GROUP ||
+        view === GerritView.REPO
+    );
+    this.set('_showCLAView', view === GerritView.AGREEMENTS);
+    this.set('_showEditorView', view === GerritView.EDIT);
+    const isPluginScreen = view === GerritView.PLUGIN_SCREEN;
     this.set('_showPluginScreen', false);
     // Navigation within plugin screens does not restamp gr-endpoint-decorator
     // because _showPluginScreen value does not change. To force restamp,
@@ -432,15 +464,23 @@
     if (isPluginScreen) {
       this.async(() => this.set('_showPluginScreen', true), 1);
     }
-    this.set('_showDocumentationSearch',
-        view === GerritNav.View.DOCUMENTATION_SEARCH);
-    if (this.params.justRegistered) {
+    this.set(
+      '_showDocumentationSearch',
+      view === GerritView.DOCUMENTATION_SEARCH
+    );
+    if (
+      this.params &&
+      isAppElementJustRegisteredParams(this.params) &&
+      this.params.justRegistered
+    ) {
       this.loadRegistrationDialog = true;
       flush();
-      const registrationOverlay =
-        this.shadowRoot.querySelector('#registrationOverlay');
-      const registrationDialog =
-        this.shadowRoot.querySelector('#registrationDialog');
+      const registrationOverlay = this.shadowRoot!.querySelector(
+        '#registrationOverlay'
+      ) as GrOverlay;
+      const registrationDialog = this.shadowRoot!.querySelector(
+        '#registrationDialog'
+      ) as GrRegistrationDialog;
       registrationOverlay.open();
       registrationDialog.loadData().then(() => {
         registrationOverlay.refit();
@@ -448,10 +488,10 @@
     }
   }
 
-  _handleShortcutTriggered(event) {
+  _handleShortcutTriggered(event: ShortcutTriggeredEvent) {
     const {event: e, goKey, vKey} = event.detail;
     // eg: {key: "k:keydown", ..., from: "gr-diff-view"}
-    let key = `${e.key}:${e.type}`;
+    let key = `${((e as unknown) as KeyboardEvent).key}:${e.type}`;
     if (goKey) key = 'g+' + key;
     if (vKey) key = 'v+' + key;
     if (e.shiftKey) key = 'shift+' + key;
@@ -460,12 +500,13 @@
     if (e.altKey) key = 'alt+' + key;
     this.reporting.reportInteraction('shortcut-triggered', {
       key,
-      from: event.path && event.path[0]
-        && event.path[0].nodeName || 'unknown',
+      from:
+        (event.path && event.path[0] && (event.path[0] as Element).nodeName) ??
+        'unknown',
     });
   }
 
-  _handlePageError(e) {
+  _handlePageError(e: CustomEvent<PageErrorEventDetail>) {
     const props = [
       '_showChangeListView',
       '_showDashboardView',
@@ -480,7 +521,9 @@
 
     this.$.errorView.classList.add('show');
     const response = e.detail.response;
-    const err = {text: [response.status, response.statusText].join(' ')};
+    const err: ErrorInfo = {
+      text: [response.status, response.statusText].join(' '),
+    };
     if (response.status === 404) {
       err.emoji = '¯\\_(ツ)_/¯';
       this._lastError = err;
@@ -493,7 +536,7 @@
     }
   }
 
-  _handleLocationChange(e) {
+  _handleLocationChange(e: LocationChangeEvent) {
     this._updateLoginUrl();
 
     const hash = e.detail.hash.substring(1);
@@ -509,27 +552,38 @@
     if (baseUrl) {
       // Strip the canonical path from the path since needing canonical in
       // the path is unneeded and breaks the url.
-      this._loginUrl = baseUrl + '/login/' + encodeURIComponent(
-          '/' + window.location.pathname.substring(baseUrl.length) +
-          window.location.search +
-          window.location.hash);
+      this._loginUrl =
+        baseUrl +
+        '/login/' +
+        encodeURIComponent(
+          '/' +
+            window.location.pathname.substring(baseUrl.length) +
+            window.location.search +
+            window.location.hash
+        );
     } else {
-      this._loginUrl = '/login/' + encodeURIComponent(
+      this._loginUrl =
+        '/login/' +
+        encodeURIComponent(
           window.location.pathname +
-          window.location.search +
-          window.location.hash);
+            window.location.search +
+            window.location.hash
+        );
     }
   }
 
-  _paramsChanged(paramsRecord) {
+  @observe('params.*')
+  _paramsChanged(
+    paramsRecord: ElementPropertyDeepChange<GrAppElement, 'params'>
+  ) {
     const params = paramsRecord.base;
-    const viewsToCheck = [GerritNav.View.SEARCH, GerritNav.View.DASHBOARD];
-    if (viewsToCheck.includes(params.view)) {
+    const viewsToCheck = [GerritView.SEARCH, GerritView.DASHBOARD];
+    if (params?.view && viewsToCheck.includes(params.view)) {
       this.set('_lastSearchPage', location.pathname);
     }
   }
 
-  _handleTitleChange(e) {
+  _handleTitleChange(e: CustomEvent<TitleChangeEventDetail>) {
     if (e.detail.title) {
       document.title = e.detail.title + ' · Gerrit Code Review';
     } else {
@@ -540,39 +594,48 @@
   handleShowKeyboardShortcuts() {
     this.loadKeyboardShortcutsDialog = true;
     flush();
-    this.shadowRoot.querySelector('#keyboardShortcuts').open();
+    (this.shadowRoot!.querySelector('#keyboardShortcuts') as GrOverlay).open();
   }
 
-  _showKeyboardShortcuts(e) {
+  _showKeyboardShortcuts(e: CustomKeyboardEvent) {
     // same shortcut should close the dialog if pressed again
     // when dialog is open
     this.loadKeyboardShortcutsDialog = true;
     flush();
-    const keyboardShortcuts =
-      this.shadowRoot.querySelector('#keyboardShortcuts');
+    const keyboardShortcuts = this.shadowRoot!.querySelector(
+      '#keyboardShortcuts'
+    ) as GrOverlay;
     if (!keyboardShortcuts) return;
     if (keyboardShortcuts.opened) {
       keyboardShortcuts.close();
       return;
     }
-    if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+    if (this.shouldSuppressKeyboardShortcut(e)) {
+      return;
+    }
     keyboardShortcuts.open();
   }
 
   _handleKeyboardShortcutDialogClose() {
-    this.shadowRoot.querySelector('#keyboardShortcuts').close();
+    (this.shadowRoot!.querySelector('#keyboardShortcuts') as GrOverlay).close();
   }
 
-  _handleAccountDetailUpdate(e) {
+  _handleAccountDetailUpdate() {
     this.$.mainHeader.reload();
-    if (this.params.view === GerritNav.View.SETTINGS) {
-      this.shadowRoot.querySelector('gr-settings-view').reloadAccountDetail();
+    if (this.params?.view === GerritView.SETTINGS) {
+      (this.shadowRoot!.querySelector(
+        'gr-settings-view'
+      ) as GrSettingsView).reloadAccountDetail();
     }
   }
 
-  _handleRegistrationDialogClose(e) {
-    this.params.justRegistered = false;
-    this.shadowRoot.querySelector('#registrationOverlay').close();
+  _handleRegistrationDialogClose() {
+    // The registration dialog is visible only if this.params is
+    // instanceof AppElementJustRegisteredParams
+    (this.params as AppElementJustRegisteredParams).justRegistered = false;
+    (this.shadowRoot!.querySelector(
+      '#registrationOverlay'
+    ) as GrOverlay).close();
   }
 
   _goToOpenedChanges() {
@@ -596,9 +659,10 @@
     GerritNav.navigateToSearchQuery('is:watched is:open');
   }
 
-  _computePluginScreenName({plugin, screen}) {
-    if (!plugin || !screen) return '';
-    return `${plugin}-screen-${screen}`;
+  _computePluginScreenName(params: AppElementParams) {
+    if (params.view !== GerritView.PLUGIN_SCREEN) return '';
+    if (!params.plugin || !params.screen) return '';
+    return `${params.plugin}-screen-${params.screen}`;
   }
 
   _logWelcome() {
@@ -619,21 +683,24 @@
    * Note: the REST API interface cannot use gr-reporting directly because
    * that would create a cyclic dependency.
    */
-  _handleRpcLog(e) {
-    this.reporting.reportRpcTiming(e.detail.anonymizedUrl,
-        e.detail.elapsed);
+  _handleRpcLog(e: RpcLogEvent) {
+    this.reporting.reportRpcTiming(e.detail.anonymizedUrl, e.detail.elapsed);
   }
 
-  _mobileSearchToggle(e) {
+  _mobileSearchToggle() {
     this.mobileSearch = !this.mobileSearch;
   }
 
   getThemeEndpoint() {
     // For now, we only have dark mode and light mode
-    return window.localStorage.getItem('dark-theme') ?
-      'app-theme-dark' :
-      'app-theme-light';
+    return window.localStorage.getItem('dark-theme')
+      ? 'app-theme-dark'
+      : 'app-theme-light';
   }
 }
 
-customElements.define(GrAppElement.is, GrAppElement);
+declare global {
+  interface HTMLElementTagNameMap {
+    'gr-app-element': GrAppElement;
+  }
+}
diff --git a/polygerrit-ui/app/elements/gr-app-types.ts b/polygerrit-ui/app/elements/gr-app-types.ts
index 29eef48..b05117f 100644
--- a/polygerrit-ui/app/elements/gr-app-types.ts
+++ b/polygerrit-ui/app/elements/gr-app-types.ts
@@ -74,8 +74,8 @@
 
 export interface AppElementPluginScreenParams {
   view: GerritView.PLUGIN_SCREEN;
-  plugin: string;
-  screen: string;
+  plugin?: string;
+  screen?: string;
 }
 
 export interface AppElementSearchParam {
@@ -122,7 +122,7 @@
   // Otherwise, the compiler reports an error when the code tries to use
   // the property 'view' of AppElementParams.
   view?: never;
-  justRegistered: true;
+  justRegistered: boolean;
 }
 
 export type AppElementParams =
@@ -136,5 +136,11 @@
   | AppElementSearchParam
   | AppElementSettingsParam
   | AppElementAgreementParam
-  | AppElementJustRegisteredParams
-  | AppElementDiffViewParam;
+  | AppElementDiffViewParam
+  | AppElementJustRegisteredParams;
+
+export function isAppElementJustRegisteredParams(
+  p: AppElementParams
+): p is AppElementJustRegisteredParams {
+  return (p as AppElementJustRegisteredParams).justRegistered !== undefined;
+}
diff --git a/polygerrit-ui/app/elements/gr-app.ts b/polygerrit-ui/app/elements/gr-app.ts
index 2801296..f19931f 100644
--- a/polygerrit-ui/app/elements/gr-app.ts
+++ b/polygerrit-ui/app/elements/gr-app.ts
@@ -15,16 +15,11 @@
  * limitations under the License.
  */
 
-import {safeTypesBridge} from '../utils/safe-types-util.js';
-
-// 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';
+import {safeTypesBridge} from '../utils/safe-types-util';
+import './gr-app-init';
+import './font-roboto-local-loader';
 // Sets up global Polymer variable, because plugins requires it.
-import '../scripts/bundled-polymer.js';
+import '../scripts/bundled-polymer';
 
 /**
  * setCancelSyntheticClickEvents is set to true by
@@ -32,38 +27,37 @@
  * on older touch device.
  * See https://github.com/Polymer/polymer/issues/5289
  */
-import {setPassiveTouchGestures, setCancelSyntheticClickEvents} from '@polymer/polymer/lib/utils/settings.js';
+import {
+  setPassiveTouchGestures,
+  setCancelSyntheticClickEvents,
+} from '@polymer/polymer/lib/utils/settings';
 setCancelSyntheticClickEvents(false);
 setPassiveTouchGestures(true);
 
-import 'polymer-resin/standalone/polymer-resin.js';
-import {initGlobalVariables} from './gr-app-global-var-init.js';
-import './gr-app-element.js';
-import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
-import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
-import {PolymerElement} from '@polymer/polymer/polymer-element.js';
-import {htmlTemplate} from './gr-app_html.js';
-import {initGerritPluginApi} from './shared/gr-js-api-interface/gr-gerrit.js';
-import {appContext} from '../services/app-context.js';
+import {initGlobalVariables} from './gr-app-global-var-init';
+import './gr-app-element';
+import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners';
+import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
+import {PolymerElement} from '@polymer/polymer/polymer-element';
+import {htmlTemplate} from './gr-app_html';
+import {initGerritPluginApi} from './shared/gr-js-api-interface/gr-gerrit';
+import {customElement} from '@polymer/decorators';
+import {installPolymerResin} from '../scripts/polymer-resin-install';
 
-security.polymer_resin.install({
-  allowedIdentifierPrefixes: [''],
-  reportHandler: security.polymer_resin.CONSOLE_LOGGING_REPORT_HANDLER,
-  safeTypesBridge,
-});
+installPolymerResin(safeTypesBridge);
 
-/** @extends PolymerElement */
-class GrApp extends GestureEventListeners(
-    LegacyElementMixin(
-        PolymerElement)) {
-  // When you are converting gr-app.js to ts, implement interface AppElement
-  // from the gr-app-types.ts
-  static get template() { return htmlTemplate; }
-
-  static get is() { return 'gr-app'; }
+@customElement('gr-app')
+class GrApp extends GestureEventListeners(LegacyElementMixin(PolymerElement)) {
+  static get template() {
+    return htmlTemplate;
+  }
 }
 
-customElements.define(GrApp.is, GrApp);
+declare global {
+  interface HTMLElementTagNameMap {
+    'gr-app': GrApp;
+  }
+}
 
 initGlobalVariables();
-initGerritPluginApi(appContext);
+initGerritPluginApi();
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.ts b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.ts
index 10335b8..02fcdec 100644
--- a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.ts
+++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.ts
@@ -15,10 +15,6 @@
  * limitations under the License.
  */
 
-export interface EventWithPath extends Event {
-  path?: HTMLElement[];
-}
-
 export interface ListenOptions {
   event?: string;
   capture?: boolean;
@@ -71,11 +67,11 @@
   _listen(
     container: HTMLElement,
     callback: (event: Event) => boolean,
-    opt_options?: ListenOptions | null
+    options?: ListenOptions | null
   ) {
-    const capture = opt_options?.capture;
-    const event = opt_options?.event || 'click';
-    const handler = (e: EventWithPath) => {
+    const capture = options?.capture;
+    const event = options?.event || 'click';
+    const handler = (e: Event) => {
       if (!e.path) return;
       if (e.path.indexOf(this.element) !== -1) {
         let mayContinue = true;
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts
index cd0d7b8..9f9840b 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts
@@ -67,7 +67,7 @@
 import {GrGpgEditor} from '../gr-gpg-editor/gr-gpg-editor';
 import {GerritView} from '../../core/gr-navigation/gr-navigation';
 import {GrEmailEditor} from '../gr-email-editor/gr-email-editor';
-import {CustomKeyboardEvent} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
+import {CustomKeyboardEvent} from '../../../types/events';
 
 const PREFS_SECTION_FIELDS: Array<keyof PreferencesInput> = [
   'changes_per_page',
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.ts b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.ts
index 033b617..668ea1b 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.ts
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.ts
@@ -24,15 +24,12 @@
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
 import {PolymerElement} from '@polymer/polymer/polymer-element';
 import {htmlTemplate} from './gr-autocomplete_html';
-import {
-  KeyboardShortcutMixin,
-  CustomKeyboardEvent,
-} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
+import {KeyboardShortcutMixin} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
 import {property, customElement, observe} from '@polymer/decorators';
 import {GrAutocompleteDropdown} from '../gr-autocomplete-dropdown/gr-autocomplete-dropdown';
 import {GrCursorManager} from '../gr-cursor-manager/gr-cursor-manager';
-import {EventWithPath} from '../../plugins/gr-event-helper/gr-event-helper';
 import {PaperInputElementExt} from '../../../types/types';
+import {CustomKeyboardEvent} from '../../../types/events';
 
 const TOKENIZE_REGEX = /(?:[^\s"]+|"[^"]*")+/g;
 const DEBOUNCE_WAIT_MS = 200;
@@ -452,7 +449,7 @@
     }
   }
 
-  _handleBodyClick(e: EventWithPath) {
+  _handleBodyClick(e: Event) {
     const eventPath = e.path;
     if (!eventPath) return;
     for (let i = 0; i < eventPath.length; i++) {
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts b/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts
index 4d75138..7a6ce2c 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts
@@ -22,13 +22,11 @@
 import {customElement, property, computed, observe} from '@polymer/decorators';
 import {htmlTemplate} from './gr-button_html';
 import {TooltipMixin} from '../../../mixins/gr-tooltip-mixin/gr-tooltip-mixin';
-import {
-  KeyboardShortcutMixin,
-  CustomKeyboardEvent,
-} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
+import {KeyboardShortcutMixin} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
 import {PolymerEvent, getEventPath} from '../../../utils/dom-util';
 import {appContext} from '../../../services/app-context';
 import {ReportingService} from '../../../services/gr-reporting/gr-reporting';
+import {CustomKeyboardEvent} from '../../../types/events';
 
 declare global {
   interface HTMLElementTagNameMap {
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
index a9df3f9..878c01c 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
@@ -23,10 +23,7 @@
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
 import {PolymerElement} from '@polymer/polymer/polymer-element';
 import {htmlTemplate} from './gr-comment-thread_html';
-import {
-  CustomKeyboardEvent,
-  KeyboardShortcutMixin,
-} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
+import {KeyboardShortcutMixin} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
 import {
   isDraft,
   isRobot,
@@ -52,6 +49,7 @@
 import {GrComment} from '../gr-comment/gr-comment';
 import {PolymerDeepPropertyChange} from '@polymer/polymer/interfaces';
 import {GrStorage, StorageLocation} from '../gr-storage/gr-storage';
+import {CustomKeyboardEvent} from '../../../types/events';
 
 const UNRESOLVED_EXPAND_COUNT = 5;
 const NEWLINE_PATTERN = /\n/g;
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
index 9a1d789..e4d520f 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
@@ -59,6 +59,7 @@
   UIDraft,
   UIRobot,
 } from '../../../utils/comment-util';
+import {OpenFixPreviewEventDetail} from '../../../types/events';
 
 const STORAGE_DEBOUNCE_INTERVAL = 400;
 const TOAST_DEBOUNCE_INTERVAL = 200;
@@ -103,11 +104,6 @@
   };
 }
 
-export interface CommentEventDetail {
-  patchNum?: PatchSetNum;
-  comment?: UIComment;
-}
-
 @customElement('gr-comment')
 export class GrComment extends KeyboardShortcutMixin(
   GestureEventListeners(LegacyElementMixin(PolymerElement))
@@ -506,7 +502,7 @@
     );
   }
 
-  _getEventPayload(): CommentEventDetail {
+  _getEventPayload(): OpenFixPreviewEventDetail {
     return {comment: this.comment, patchNum: this.patchNum};
   }
 
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.ts b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.ts
index edadfba..9e1a5bf 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.ts
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.ts
@@ -21,15 +21,13 @@
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
 import {PolymerElement} from '@polymer/polymer/polymer-element';
-import {
-  CustomKeyboardEvent,
-  KeyboardShortcutMixin,
-} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
+import {KeyboardShortcutMixin} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
 import {customElement, property} from '@polymer/decorators';
 import {htmlTemplate} from './gr-editable-label_html';
 import {IronDropdownElement} from '@polymer/iron-dropdown/iron-dropdown';
 import {dom, EventApi} from '@polymer/polymer/lib/legacy/polymer.dom';
 import {PaperInputElementExt} from '../../../types/types';
+import {CustomKeyboardEvent} from '../../../types/events';
 
 const AWAIT_MAX_ITERS = 10;
 const AWAIT_STEP = 5;
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.ts b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.ts
index eee48d2..6d93604 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.ts
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.ts
@@ -32,6 +32,7 @@
   RequestPayload,
 } from '../../../../types/common';
 import {HttpMethod} from '../../../../constants/constants';
+import {RpcLogEventDetail} from '../../../../types/events';
 
 const JSON_PREFIX = ")]}'";
 
@@ -270,9 +271,15 @@
       ].join(' ')
     );
     if (req.anonymizedUrl) {
+      const detail: RpcLogEventDetail = {
+        status,
+        method,
+        elapsed,
+        anonymizedUrl: req.anonymizedUrl,
+      };
       this.dispatchEvent(
         new CustomEvent('rpc-log', {
-          detail: {status, method, elapsed, anonymizedUrl: req.anonymizedUrl},
+          detail,
           composed: true,
           bubbles: true,
         })