Convert files to typescript

The change converts the following files to typescript:

* elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts

Change-Id: I274ba656285897c89c42f299724e402758b8cfe3
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.ts b/polygerrit-ui/app/elements/change/gr-message/gr-message.ts
index 2b5d94d..05316db 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.ts
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.ts
@@ -38,6 +38,7 @@
   RepoName,
   ReviewInputTag,
   VotingRangeInfo,
+  ChangeNum,
 } from '../../../types/common';
 import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
 import {CommentThread} from '../../diff/gr-comment-api/gr-comment-api';
@@ -102,7 +103,7 @@
   change?: ChangeInfo;
 
   @property({type: Number})
-  changeNum?: number;
+  changeNum?: ChangeNum;
 
   @property({type: Object})
   message: ChangeMessage | undefined;
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.ts b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.ts
index 11e4888..db0bd64 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.ts
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.ts
@@ -596,7 +596,7 @@
    * @param basePatchNum The string 'PARENT' can be used for none.
    */
   getUrlForChange(
-    change: ChangeInfo,
+    change: Pick<ChangeInfo, '_number' | 'project' | 'internalHost'>,
     patchNum?: PatchSetNum,
     basePatchNum?: PatchSetNum,
     isEdit?: boolean,
@@ -640,7 +640,7 @@
    *
    */
   navigateToChange(
-    change: ChangeInfo,
+    change: Pick<ChangeInfo, '_number' | 'project' | 'internalHost'>,
     patchNum?: PatchSetNum,
     basePatchNum?: PatchSetNum,
     isEdit?: boolean,
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 d051d30..4ac7e8f 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
@@ -14,87 +14,131 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '@polymer/iron-icon/iron-icon.js';
-import '../../../styles/shared-styles.js';
-import '../../shared/gr-dialog/gr-dialog.js';
-import '../../shared/gr-overlay/gr-overlay.js';
-import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
-import '../gr-diff/gr-diff.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-apply-fix-dialog_html.js';
-import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
+import '@polymer/iron-icon/iron-icon';
+import '../../../styles/shared-styles';
+import '../../shared/gr-dialog/gr-dialog';
+import '../../shared/gr-overlay/gr-overlay';
+import '../../shared/gr-rest-api-interface/gr-rest-api-interface';
+import '../gr-diff/gr-diff';
+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-apply-fix-dialog_html';
+import {GerritNav} from '../../core/gr-navigation/gr-navigation';
+import {customElement, property} from '@polymer/decorators';
+import {ParsedChangeInfo} from '../../shared/gr-rest-api-interface/gr-reviewer-updates-parser';
+import {
+  ChangeNum,
+  DiffInfo,
+  DiffPreferencesInfo,
+  EditPatchSetNum,
+  FixId,
+  FixSuggestionInfo,
+  PatchSetNum,
+  RobotId,
+} from '../../../types/common';
+import {GrOverlay} from '../../shared/gr-overlay/gr-overlay';
+import {
+  CommentEventDetail,
+  isRobotComment,
+} from '../../shared/gr-comment/gr-comment';
+import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
 
-/**
- * @extends PolymerElement
- */
-class GrApplyFixDialog extends GestureEventListeners(
-    LegacyElementMixin(PolymerElement)) {
-  static get template() { return htmlTemplate; }
+export interface GrApplyFixDialog {
+  $: {
+    restAPI: RestApiService & Element;
+    applyFixOverlay: GrOverlay;
+  };
+}
 
-  static get is() { return 'gr-apply-fix-dialog'; }
+interface FilePreview {
+  filepath: string;
+  preview: DiffInfo;
+}
 
-  static get properties() {
-    return {
-      // Diff rendering preference API response.
-      prefs: Array,
-      // ChangeInfo API response object.
-      change: Object,
-      changeNum: String,
-      _patchNum: Number,
-      // robot ID associated with a robot comment.
-      _robotId: String,
-      // Selected FixSuggestionInfo entity from robot comment API response.
-      _currentFix: Object,
-      // Flattened /preview API response DiffInfo map object.
-      _currentPreviews: {type: Array, value: () => []},
-      // FixSuggestionInfo entities from robot comment API response.
-      _fixSuggestions: Array,
-      _isApplyFixLoading: {
-        type: Boolean,
-        value: false,
-      },
-      // Index of currently showing suggested fix.
-      _selectedFixIdx: Number,
-      _disableApplyFixButton: {
-        type: Boolean,
-        computed: '_computeDisableApplyFixButton(_isApplyFixLoading, change, '
-          + '_patchNum)',
-      },
-    };
+@customElement('gr-apply-fix-dialog')
+export class GrApplyFixDialog extends GestureEventListeners(
+  LegacyElementMixin(PolymerElement)
+) {
+  static get template() {
+    return htmlTemplate;
   }
 
+  @property({type: Object})
+  prefs?: DiffPreferencesInfo;
+
+  @property({type: Object})
+  change?: ParsedChangeInfo;
+
+  @property({type: String})
+  changeNum?: ChangeNum;
+
+  @property({type: Number})
+  _patchNum?: PatchSetNum;
+
+  @property({type: String})
+  _robotId?: RobotId;
+
+  @property({type: Object})
+  _currentFix?: FixSuggestionInfo;
+
+  @property({type: Array})
+  _currentPreviews: FilePreview[] = [];
+
+  @property({type: Array})
+  _fixSuggestions?: FixSuggestionInfo[];
+
+  @property({type: Boolean})
+  _isApplyFixLoading = false;
+
+  @property({type: Number})
+  _selectedFixIdx = 0;
+
+  @property({
+    type: Boolean,
+    computed:
+      '_computeDisableApplyFixButton(_isApplyFixLoading, change, ' +
+      '_patchNum)',
+  })
+  _disableApplyFixButton?: boolean;
+
+  private refitOverlay?: () => void;
+
   /**
-   * Given robot comment CustomEvent objevt, fetch diffs associated
+   * Given robot comment CustomEvent object, fetch diffs associated
    * with first robot comment suggested fix and open dialog.
    *
-   * @param {*} e CustomEvent to be passed from gr-comment with
-   * robot comment detail.
-   * @return {Promise<undefined>} Promise that resolves either when all
+   * @param e to be passed from gr-comment with robot comment detail.
+   * @return Promise that resolves either when all
    * preview diffs are fetched or no fix suggestions in custom event detail.
    */
-  open(e) {
-    this._patchNum = e.detail.patchNum;
-    this._fixSuggestions = e.detail.comment.fix_suggestions;
-    this._robotId = e.detail.comment.robot_id;
-    if (this._fixSuggestions == null || this._fixSuggestions.length == 0) {
+  open(e: CustomEvent<CommentEventDetail>) {
+    const detail = e.detail;
+    const comment = detail.comment;
+    if (!detail.patchNum || !comment || !isRobotComment(comment)) {
+      return Promise.resolve();
+    }
+    this._patchNum = detail.patchNum;
+    this._fixSuggestions = comment.fix_suggestions;
+    this._robotId = comment.robot_id;
+    if (!this._fixSuggestions || !this._fixSuggestions.length) {
       return Promise.resolve();
     }
     this._selectedFixIdx = 0;
     const promises = [];
     promises.push(
-        this._showSelectedFixSuggestion(this._fixSuggestions[0]),
-        this.$.applyFixOverlay.open()
+      this._showSelectedFixSuggestion(this._fixSuggestions[0]),
+      this.$.applyFixOverlay.open()
     );
-    return Promise.all(promises)
-        .then(() => {
-          // ensures gr-overlay repositions overlay in center
-          this.$.applyFixOverlay.dispatchEvent(
-              new CustomEvent('iron-resize', {
-                composed: true, bubbles: true,
-              }));
-        });
+    return Promise.all(promises).then(() => {
+      // ensures gr-overlay repositions overlay in center
+      this.$.applyFixOverlay.dispatchEvent(
+        new CustomEvent('iron-resize', {
+          composed: true,
+          bubbles: true,
+        })
+      );
+    });
   }
 
   attached() {
@@ -102,114 +146,132 @@
     this.refitOverlay = () => {
       // re-center the dialog as content changed
       this.$.applyFixOverlay.dispatchEvent(
-          new CustomEvent('iron-resize', {
-            composed: true, bubbles: true,
-          }));
+        new CustomEvent('iron-resize', {
+          composed: true,
+          bubbles: true,
+        })
+      );
     };
     this.addEventListener('diff-context-expanded', this.refitOverlay);
   }
 
   detached() {
     super.detached();
-    this.removeEventListener('diff-context-expanded', this.refitOverlay);
+    if (this.refitOverlay) {
+      this.removeEventListener('diff-context-expanded', this.refitOverlay);
+    }
   }
 
-  _showSelectedFixSuggestion(fixSuggestion) {
+  _showSelectedFixSuggestion(fixSuggestion: FixSuggestionInfo) {
     this._currentFix = fixSuggestion;
     return this._fetchFixPreview(fixSuggestion.fix_id);
   }
 
-  _fetchFixPreview(fixId) {
+  _fetchFixPreview(fixId: FixId) {
+    if (!this.changeNum || !this._patchNum) {
+      return Promise.reject(
+        new Error('Both _patchNum and changeNum must be set')
+      );
+    }
     return this.$.restAPI
-        .getRobotCommentFixPreview(this.changeNum, this._patchNum, fixId)
-        .then(res => {
-          if (res != null) {
-            this._currentPreviews = Object.keys(res).map(key => {
-              return {filepath: key, preview: res[key]};
-            });
-          }
-        })
-        .catch(err => {
-          this._close();
-          throw err;
-        });
+      .getRobotCommentFixPreview(this.changeNum, this._patchNum, fixId)
+      .then(res => {
+        if (res) {
+          this._currentPreviews = Object.keys(res).map(key => {
+            return {filepath: key, preview: res[key]};
+          });
+        }
+      })
+      .catch(err => {
+        this._close();
+        throw err;
+      });
   }
 
-  hasSingleFix(_fixSuggestions) {
-    return (_fixSuggestions || {}).length === 1;
+  hasSingleFix(_fixSuggestions?: FixSuggestionInfo[]) {
+    return (_fixSuggestions || []).length === 1;
   }
 
-  overridePartialPrefs(prefs) {
+  overridePartialPrefs(prefs: DiffPreferencesInfo): DiffPreferencesInfo {
     // generate a smaller gr-diff than fullscreen for dialog
     return {...prefs, line_length: 50};
   }
 
-  onCancel(e) {
+  onCancel(e: CustomEvent) {
     if (e) {
       e.stopPropagation();
     }
     this._close();
   }
 
-  addOneTo(_selectedFixIdx) {
+  addOneTo(_selectedFixIdx: number) {
     return _selectedFixIdx + 1;
   }
 
-  _onPrevFixClick(e) {
+  _onPrevFixClick(e: CustomEvent) {
     if (e) e.stopPropagation();
-    if (this._selectedFixIdx >= 1 && this._fixSuggestions != null) {
+    if (this._selectedFixIdx >= 1 && this._fixSuggestions) {
       this._selectedFixIdx -= 1;
-      return this._showSelectedFixSuggestion(
-          this._fixSuggestions[this._selectedFixIdx]);
+      this._showSelectedFixSuggestion(
+        this._fixSuggestions[this._selectedFixIdx]
+      );
     }
   }
 
-  _onNextFixClick(e) {
+  _onNextFixClick(e: CustomEvent) {
     if (e) e.stopPropagation();
-    if (this._fixSuggestions &&
-      this._selectedFixIdx < this._fixSuggestions.length) {
+    if (
+      this._fixSuggestions &&
+      this._selectedFixIdx < this._fixSuggestions.length
+    ) {
       this._selectedFixIdx += 1;
-      return this._showSelectedFixSuggestion(
-          this._fixSuggestions[this._selectedFixIdx]);
+      this._showSelectedFixSuggestion(
+        this._fixSuggestions[this._selectedFixIdx]
+      );
     }
   }
 
-  _noPrevFix(_selectedFixIdx) {
+  _noPrevFix(_selectedFixIdx: number) {
     return _selectedFixIdx === 0;
   }
 
-  _noNextFix(_selectedFixIdx, fixSuggestions) {
-    if (fixSuggestions == null) return true;
+  _noNextFix(_selectedFixIdx: number, fixSuggestions?: FixSuggestionInfo[]) {
+    if (!fixSuggestions) return true;
     return _selectedFixIdx === fixSuggestions.length - 1;
   }
 
   _close() {
-    this._currentFix = {};
+    this._currentFix = undefined;
     this._currentPreviews = [];
     this._isApplyFixLoading = false;
 
-    this.dispatchEvent(new CustomEvent('close-fix-preview', {
-      bubbles: true,
-      composed: true,
-    }));
+    this.dispatchEvent(
+      new CustomEvent('close-fix-preview', {
+        bubbles: true,
+        composed: true,
+      })
+    );
     this.$.applyFixOverlay.close();
   }
 
-  _getApplyFixButtonLabel(isLoading) {
+  _getApplyFixButtonLabel(isLoading: boolean) {
     return isLoading ? 'Saving...' : 'Apply Fix';
   }
 
-  _computeTooltip(change, patchNum) {
-    if (!change || patchNum == undefined) return '';
-    // If change is defined, change.revisions and change.current_revisions
-    // must be defined
+  _computeTooltip(change?: ParsedChangeInfo, patchNum?: PatchSetNum) {
+    if (!change || !patchNum) return '';
     const latestPatchNum = change.revisions[change.current_revision]._number;
-    return latestPatchNum !== patchNum ?
-      'Fix can only be applied to the latest patchset' : '';
+    return latestPatchNum !== patchNum
+      ? 'Fix can only be applied to the latest patchset'
+      : '';
   }
 
-  _computeDisableApplyFixButton(isApplyFixLoading, change, patchNum) {
-    if (!change || isApplyFixLoading == undefined || patchNum == undefined) {
+  _computeDisableApplyFixButton(
+    isApplyFixLoading?: boolean,
+    change?: ParsedChangeInfo,
+    patchNum?: PatchSetNum
+  ) {
+    if (!change || isApplyFixLoading === undefined || patchNum === undefined) {
       return true;
     }
     const currentPatchNum = change.revisions[change.current_revision]._number;
@@ -219,31 +281,36 @@
     return isApplyFixLoading;
   }
 
-  _handleApplyFix(e) {
+  _handleApplyFix(e: CustomEvent) {
     if (e) {
       e.stopPropagation();
     }
-    if (this._currentFix == null || this._currentFix.fix_id == null) {
-      return;
+
+    const changeNum = this.changeNum;
+    const patchNum = this._patchNum;
+    const change = this.change;
+    if (!changeNum || !patchNum || !change || !this._currentFix) {
+      return Promise.reject(new Error('Not all required properties are set.'));
     }
     this._isApplyFixLoading = true;
     return this.$.restAPI
-        .applyFixSuggestion(
-            this.changeNum, this._patchNum, this._currentFix.fix_id
-        )
-        .then(res => {
-          if (res && res.ok) {
-            GerritNav.navigateToChange(this.change, 'edit', this._patchNum);
-            this._close();
-          }
-          this._isApplyFixLoading = false;
-        });
+      .applyFixSuggestion(changeNum, patchNum, this._currentFix.fix_id)
+      .then(res => {
+        if (res && res.ok) {
+          GerritNav.navigateToChange(change, EditPatchSetNum, patchNum);
+          this._close();
+        }
+        this._isApplyFixLoading = false;
+      });
   }
 
-  getFixDescription(currentFix) {
-    return currentFix != null && currentFix.description ?
-      currentFix.description : '';
+  getFixDescription(currentFix?: FixSuggestionInfo) {
+    return currentFix && currentFix.description ? currentFix.description : '';
   }
 }
 
-customElements.define(GrApplyFixDialog.is, GrApplyFixDialog);
+declare global {
+  interface HTMLElementTagNameMap {
+    'gr-apply-fix-dialog': GrApplyFixDialog;
+  }
+}
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.js b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.js
index b3ba637..cb73885 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.js
@@ -179,19 +179,19 @@
       comment: ROBOT_COMMENT_WITH_TWO_FIXES}});
     flush(() => {
       assert.isTrue(errorStub.called);
-      assert.deepEqual(element._currentFix, {});
+      assert.equal(element._currentFix, undefined);
       done();
     });
   });
 
   test('apply fix button should call apply ' +
-  'and navigate to change view', done => {
+  'and navigate to change view', () => {
     sinon.stub(element.$.restAPI, 'applyFixSuggestion')
         .returns(Promise.resolve({ok: true}));
     sinon.stub(GerritNav, 'navigateToChange');
     element._currentFix = {fix_id: '123'};
 
-    element._handleApplyFix().then(() => {
+    return element._handleApplyFix().then(() => {
       assert.isTrue(element.$.restAPI.applyFixSuggestion
           .calledWithExactly('1', 2, '123'));
       assert.isTrue(GerritNav.navigateToChange.calledWithExactly({
@@ -205,9 +205,8 @@
       }, 'edit', 2));
 
       // reset gr-apply-fix-dialog and close
-      assert.deepEqual(element._currentFix, {});
+      assert.equal(element._currentFix, undefined);
       assert.equal(element._currentPreviews.length, 0);
-      done();
     });
   });
 
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts
index b75c26d..88b0f71 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts
@@ -40,6 +40,7 @@
   PatchSetNum,
   EditPreferencesInfo,
   Base64FileContent,
+  ChangeNum,
 } from '../../../types/common';
 import {GrStorage} from '../../shared/gr-storage/gr-storage';
 
@@ -83,7 +84,7 @@
   _change?: ChangeInfo | null;
 
   @property({type: Number})
-  _changeNum?: number;
+  _changeNum?: ChangeNum;
 
   @property({type: String})
   _patchNum?: PatchSetNum;
@@ -189,7 +190,7 @@
     return Promise.all(promises);
   }
 
-  _getChangeDetail(changeNum: number) {
+  _getChangeDetail(changeNum: ChangeNum) {
     return this.$.restAPI.getDiffChangeDetail(changeNum).then(change => {
       this._change = change;
     });
@@ -229,7 +230,7 @@
       );
   }
 
-  _getFileData(changeNum: number, path: string, patchNum?: PatchSetNum) {
+  _getFileData(changeNum: ChangeNum, path: string, patchNum?: PatchSetNum) {
     if (patchNum === undefined) {
       return Promise.reject(new Error('patchNum undefined'));
     }
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 bc7e739..f1af68d 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
@@ -49,6 +49,7 @@
   CommentInfo,
   ConfigInfo,
   AccountDetailInfo,
+  ChangeNum,
 } from '../../../types/common';
 import {GrButton} from '../gr-button/gr-button';
 import {GrConfirmDeleteCommentDialog} from '../gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog';
@@ -96,6 +97,10 @@
 export type Comment = Draft & CommentInfo;
 export type RobotComment = Draft & RobotCommentInfo;
 
+export function isRobotComment(c: Comment | RobotComment): c is RobotComment {
+  return (c as RobotComment).robot_id !== undefined;
+}
+
 interface CommentOverlays {
   confirmDelete?: GrOverlay | null;
   confirmDiscard?: GrOverlay | null;
@@ -109,6 +114,12 @@
     resolvedCheckbox: HTMLInputElement;
   };
 }
+
+export interface CommentEventDetail {
+  patchNum?: PatchSetNum;
+  comment?: Comment | RobotComment;
+}
+
 @customElement('gr-comment')
 export class GrComment extends KeyboardShortcutMixin(
   GestureEventListeners(LegacyElementMixin(PolymerElement))
@@ -160,7 +171,7 @@
    */
 
   @property({type: Number})
-  changeNum?: number;
+  changeNum?: ChangeNum;
 
   @property({type: Object, notify: true, observer: '_commentChanged'})
   comment?: Comment | RobotComment;
@@ -506,8 +517,8 @@
     );
   }
 
-  _getEventPayload(opt_mixin?: Record<string, any>) {
-    return {...opt_mixin, comment: this.comment, patchNum: this.patchNum};
+  _getEventPayload(): CommentEventDetail {
+    return {comment: this.comment, patchNum: this.patchNum};
   }
 
   _fireSave() {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts
index 944c0a7..b3dba32 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts
@@ -130,6 +130,9 @@
   SubmittedTogetherInfo,
   ChangeNum,
   EmailAddress,
+  FixId,
+  FilePathToDiffInfoMap,
+  ChangeViewChangeInfo,
 } from '../../../types/common';
 import {
   CancelConditionCallback,
@@ -1388,7 +1391,11 @@
         optionsHex,
         errFn,
         cancelCondition
-      ).then(GrReviewerUpdatesParser.parse);
+      ).then(detail =>
+        // detail has ChangeViewChangeInfo type because the optionsHex always
+        // includes ALL_REVISIONS flag.
+        GrReviewerUpdatesParser.parse(detail as ChangeViewChangeInfo)
+      );
     });
   }
 
@@ -2329,21 +2336,21 @@
   getRobotCommentFixPreview(
     changeNum: ChangeNum,
     patchNum: PatchSetNum,
-    fixId: string
-  ) {
+    fixId: FixId
+  ): Promise<FilePathToDiffInfoMap | undefined> {
     return this._getChangeURLAndFetch({
       changeNum,
       patchNum,
       endpoint: `/fixes/${encodeURIComponent(fixId)}/preview`,
       reportEndpointAsId: true,
-    });
+    }) as Promise<FilePathToDiffInfoMap | undefined>;
   }
 
   applyFixSuggestion(
     changeNum: ChangeNum,
     patchNum: PatchSetNum,
     fixId: string
-  ) {
+  ): Promise<Response> {
     return this._getChangeURLAndSend({
       method: HttpMethod.POST,
       changeNum,
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.ts b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.ts
index 2c3bf44..b5e5696 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.ts
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.ts
@@ -21,6 +21,7 @@
   AccountInfo,
   ChangeInfo,
   ChangeMessageInfo,
+  ChangeViewChangeInfo,
   ReviewerUpdateInfo,
   Timestamp,
 } from '../../../types/common';
@@ -30,7 +31,7 @@
 const MESSAGE_REVIEWERS_THRESHOLD_MILLIS = 500;
 const REVIEWER_UPDATE_THRESHOLD_MILLIS = 6000;
 
-interface ChangeInfoParserInput extends ChangeInfo {
+interface ChangeInfoParserInput extends ChangeViewChangeInfo {
   messages: ChangeMessageInfo[];
   reviewer_updates: ReviewerUpdateInfo[]; // Always has at least 1 item
 }
@@ -77,7 +78,8 @@
   prev_state?: ReviewerState;
 }
 
-export interface ParsedChangeInfo extends Omit<ChangeInfo, 'reviewer_updates'> {
+export interface ParsedChangeInfo
+  extends Omit<ChangeViewChangeInfo, 'reviewer_updates'> {
   reviewer_updates?: ReviewerUpdateInfo[] | FormattedReviewerUpdateInfo[];
 }
 
@@ -291,7 +293,7 @@
   }
 
   static parse(
-    change: ChangeInfo | undefined | null
+    change: ChangeViewChangeInfo | undefined | null
   ): ParsedChangeInfo | undefined | null {
     // TODO(TS): The !change condition should be removed when all files are converted to TS
     if (!change || !isChangeInfoParserInput(change)) {
diff --git a/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts b/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts
index 3876f0e..8a7180e 100644
--- a/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts
+++ b/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts
@@ -85,6 +85,8 @@
   RelatedChangesInfo,
   SubmittedTogetherInfo,
   EmailAddress,
+  FixId,
+  FilePathToDiffInfoMap,
 } from '../../../types/common';
 import {ParsedChangeInfo} from '../../../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser';
 import {HttpMethod} from '../../../constants/constants';
@@ -718,4 +720,16 @@
 
   hasPendingDiffDrafts(): number;
   awaitPendingDiffDrafts(): Promise<void>;
+
+  getRobotCommentFixPreview(
+    changeNum: ChangeNum,
+    patchNum: PatchSetNum,
+    fixId: FixId
+  ): Promise<FilePathToDiffInfoMap | undefined>;
+
+  applyFixSuggestion(
+    changeNum: ChangeNum,
+    patchNum: PatchSetNum,
+    fixId: string
+  ): Promise<Response>;
 }
diff --git a/polygerrit-ui/app/types/common.ts b/polygerrit-ui/app/types/common.ts
index f943ff5..ce2b9cb 100644
--- a/polygerrit-ui/app/types/common.ts
+++ b/polygerrit-ui/app/types/common.ts
@@ -47,6 +47,12 @@
 export type BrandType<T, BrandName extends string> = T &
   {[__brand in BrandName]: never};
 
+/*
+ * In T, make a set of properties whose keys are in the union K required
+ */
+export type RequireProperties<T, K extends keyof T> = Omit<T, K> &
+  Required<Pick<T, K>>;
+
 /**
  * Type alias for parsed json object to make code cleaner
  */
@@ -62,7 +68,7 @@
 export type ChangeId = BrandType<string, '_changeId'>;
 export type ChangeMessageId = BrandType<string, '_changeMessageId'>;
 export type NumericChangeId = BrandType<number, '_numericChangeId'>;
-export type ChangeNum = number; // !!!TODO: define correct types
+export type ChangeNum = NumericChangeId; // This type is removed in the following change
 export type RepoName = BrandType<string, '_repoName'>;
 export type UrlEncodedRepoName = BrandType<string, '_urlEncodedRepoName'>;
 export type TopicName = BrandType<string, '_topicName'>;
@@ -74,6 +80,8 @@
 export type ReviewInputTag = BrandType<string, '_reviewInputTag'>;
 export type RobotId = BrandType<string, '_robotId'>;
 export type RobotRunId = BrandType<string, '_robotRunId'>;
+
+// The UUID of the suggested fix.
 export type FixId = BrandType<string, '_fixId'>;
 export type EmailAddress = BrandType<string, '_emailAddress'>;
 
@@ -238,6 +246,14 @@
 }
 
 /**
+ * ChangeView request change detail with ALL_REVISIONS option set.
+ * The response always contains current_revision and revisions.
+ */
+export type ChangeViewChangeInfo = RequireProperties<
+  ChangeInfo,
+  'current_revision' | 'revisions'
+>;
+/**
  * The AccountInfo entity contains information about an account.
  * https://gerrit-review.googlesource.com/Documentation/rest-api-accounts.html#account-info
  */
@@ -1178,6 +1194,8 @@
   binary: boolean;
 }
 
+export type FilePathToDiffInfoMap = {[path: string]: DiffInfo};
+
 /**
  * The DiffWebLinkInfo entity describes a link on a diff screen to an external site.
  * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#diff-web-link-info
@@ -1876,8 +1894,13 @@
  * The FixSuggestionInfo entity represents a suggested fix
  * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#fix-suggestion-info
  */
-export interface FixSuggestionInfo {
-  fix_id?: FixId;
+export interface FixSuggestionInfoInput {
+  description: string;
+  replacements: FixReplacementInfo[];
+}
+
+export interface FixSuggestionInfo extends FixSuggestionInfoInput {
+  fix_id: FixId;
   description: string;
   replacements: FixReplacementInfo[];
 }
diff --git a/polygerrit-ui/app/utils/patch-set-util.ts b/polygerrit-ui/app/utils/patch-set-util.ts
index ff55e1f..4d688bd 100644
--- a/polygerrit-ui/app/utils/patch-set-util.ts
+++ b/polygerrit-ui/app/utils/patch-set-util.ts
@@ -169,7 +169,9 @@
  * @return Sorted list of patch set objects, as described
  *     above
  */
-export function computeAllPatchSets(change: ParsedChangeInfo): PatchSet[] {
+export function computeAllPatchSets(
+  change: ChangeInfo | ParsedChangeInfo
+): PatchSet[] {
   if (!change) {
     return [];
   }
@@ -205,7 +207,7 @@
  *     wip property set on each of them
  */
 function _computeWipForPatchSets(
-  change: ParsedChangeInfo,
+  change: ChangeInfo | ParsedChangeInfo,
   patchNums: PatchSet[]
 ) {
   if (!change.messages || !change.messages.length) {