Create UserId type

User can be referenced with AccountId/GroupId/Email Address.
Introduce a new type UserId as a union of all three type.

Rename accountOrGroupKey to getUserId.

Release-Notes: skip
Google-bug-id: b/236921879
Change-Id: I7f611f48ad0d300aa48517029fcbe42df6baf837
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-reviewer-flow/gr-change-list-reviewer-flow.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-reviewer-flow/gr-change-list-reviewer-flow.ts
index 16998d0..436e435 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-reviewer-flow/gr-change-list-reviewer-flow.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-reviewer-flow/gr-change-list-reviewer-flow.ts
@@ -39,7 +39,7 @@
 } from '../../shared/gr-account-list/gr-account-list';
 import {getReplyByReason} from '../../../utils/attention-set-util';
 import {intersection, queryAndAssert} from '../../../utils/common-util';
-import {accountKey, accountOrGroupKey} from '../../../utils/account-util';
+import {accountKey, getUserId} from '../../../utils/account-util';
 import {ValueChangedEvent} from '../../../types/events';
 import {fireAlert, fireReload} from '../../../utils/event-util';
 import {GrDialog} from '../../shared/gr-dialog/gr-dialog';
@@ -368,8 +368,7 @@
       .get(updatedReviewerState)!
       .filter(account =>
         accountsInCurrentState.some(
-          otherAccount =>
-            accountOrGroupKey(otherAccount) === accountOrGroupKey(account)
+          otherAccount => getUserId(otherAccount) === getUserId(account)
         )
       )
       .map(reviewer => getDisplayName(this.serverConfig, reviewer));
@@ -421,7 +420,7 @@
   private onAccountsChanged(reviewerState: ReviewerState) {
     const reviewerStateKeys = this.updatedAccountsByReviewerState
       .get(reviewerState)!
-      .map(accountOrGroupKey);
+      .map(getUserId);
     const oppositeReviewerState =
       reviewerState === ReviewerState.CC
         ? ReviewerState.REVIEWER
@@ -431,7 +430,7 @@
     )!;
 
     const notOverwrittenOppositeAccounts = oppositeUpdatedAccounts.filter(
-      acc => !reviewerStateKeys.includes(accountOrGroupKey(acc))
+      acc => !reviewerStateKeys.includes(getUserId(acc))
     );
     if (
       notOverwrittenOppositeAccounts.length !== oppositeUpdatedAccounts.length
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
index cfcc299..6659602 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
@@ -26,7 +26,7 @@
   SpecialFilePath,
 } from '../../../constants/constants';
 import {
-  accountOrGroupKey,
+  getUserId,
   isAccountNewlyAdded,
   removeServiceUsers,
   toReviewInput,
@@ -274,8 +274,7 @@
     this._ccs = ccs.filter(
       cc =>
         !this.mentionedUsers.some(
-          mentionedCC =>
-            accountOrGroupKey(mentionedCC) === accountOrGroupKey(cc)
+          mentionedCC => getUserId(mentionedCC) === getUserId(cc)
         )
     );
     this.requestUpdate('ccs', ccs);
@@ -1334,7 +1333,7 @@
   // remove account-added event from GrAccountList.
   handleAccountAdded(e: CustomEvent<AccountInputDetail>) {
     const account = e.detail.account;
-    const key = accountOrGroupKey(account);
+    const key = getUserId(account);
     const reviewerType =
       (e.target as GrAccountList).getAttribute('id') === 'ccs'
         ? ReviewerType.CC
@@ -1370,7 +1369,7 @@
     let removals = difference(
       this.reviewersList?.removals() ?? [],
       ccAdditions,
-      (a, b) => accountOrGroupKey(a) === accountOrGroupKey(b)
+      (a, b) => getUserId(a) === getUserId(b)
     ).map(v => toReviewInput(v, ReviewerState.REMOVED));
     reviewers.push(...removals);
 
@@ -1378,7 +1377,7 @@
     removals = difference(
       this.ccsList?.removals() ?? [],
       reviewerAdditions,
-      (a, b) => accountOrGroupKey(a) === accountOrGroupKey(b)
+      (a, b) => getUserId(a) === getUserId(b)
     ).map(v => toReviewInput(v, ReviewerState.REMOVED));
     reviewers.push(...removals);
 
@@ -1591,12 +1590,11 @@
     const targetAccount = (e.target as GrAccountChip)?.account;
     if (!targetAccount) return;
     // TODO: Remove cast and add support for GroupId as id type
-    const id = accountOrGroupKey(targetAccount) as AccountId | EmailAddress;
+    const id = getUserId(targetAccount) as AccountId | EmailAddress;
     if (!id || !this.account || !this.change?.owner) return;
 
-    const self = id === accountOrGroupKey(this.account) ? '_SELF' : '';
-    const role =
-      id === accountOrGroupKey(this.change.owner) ? 'OWNER' : '_REVIEWER';
+    const self = id === getUserId(this.account) ? '_SELF' : '';
+    const role = id === getUserId(this.change.owner) ? 'OWNER' : '_REVIEWER';
 
     if (this.newAttentionSet.has(id)) {
       this.newAttentionSet.delete(id);
@@ -1836,9 +1834,9 @@
         return false;
       }
 
-      const key = accountOrGroupKey(entry);
+      const key = getUserId(entry);
       const finder = (entry: AccountInfo | GroupInfo) =>
-        accountOrGroupKey(entry) === key;
+        getUserId(entry) === key;
       if (isCCs) {
         return this.ccs.find(finder) === undefined;
       }
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.ts b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.ts
index f6de258..29ed322 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.ts
@@ -13,7 +13,7 @@
   __testOnly_SortDropdownState,
 } from './gr-thread-list';
 import {queryAll, stubFlags} from '../../../test/test-utils';
-import {accountOrGroupKey} from '../../../utils/account-util';
+import {getUserId} from '../../../utils/account-util';
 import {tap} from '@polymer/iron-test-helpers/mock-interactions';
 import {
   createAccountDetailWithId,
@@ -391,7 +391,7 @@
     const chips = Array.from(
       queryAll<GrAccountLabel>(element, 'gr-account-label')
     );
-    const authors = chips.map(chip => accountOrGroupKey(chip.account!)).sort();
+    const authors = chips.map(chip => getUserId(chip.account!)).sort();
     assert.deepEqual(authors, [
       1 as AccountId,
       1000000 as AccountId,
@@ -426,7 +426,7 @@
     const chips = Array.from(
       queryAll<GrAccountLabel>(element, 'gr-account-label')
     );
-    const authors = chips.map(chip => accountOrGroupKey(chip.account!)).sort();
+    const authors = chips.map(chip => getUserId(chip.account!)).sort();
     assert.deepEqual(authors, [1 as AccountId]);
     assert.equal(element.threads.length, 1);
     assert.equal(element.getDisplayedThreads().length, 1);
diff --git a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.ts b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.ts
index 3e2a935..ad049eb 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.ts
@@ -21,10 +21,7 @@
 import {GrAccountEntry} from '../gr-account-entry/gr-account-entry';
 import {GrAccountChip} from '../gr-account-chip/gr-account-chip';
 import {fire, fireAlert} from '../../../utils/event-util';
-import {
-  accountOrGroupKey,
-  isAccountNewlyAdded,
-} from '../../../utils/account-util';
+import {getUserId, isAccountNewlyAdded} from '../../../utils/account-util';
 import {LitElement, css, html, PropertyValues} from 'lit';
 import {customElement, property, query, state} from 'lit/decorators.js';
 import {sharedStyles} from '../../../styles/shared-styles';
@@ -324,10 +321,7 @@
     }
     if (this.removableValues) {
       for (let i = 0; i < this.removableValues.length; i++) {
-        if (
-          accountOrGroupKey(this.removableValues[i]) ===
-          accountOrGroupKey(account)
-        ) {
+        if (getUserId(this.removableValues[i]) === getUserId(account)) {
           return true;
         }
       }
@@ -347,7 +341,7 @@
       return false;
     }
     for (let i = 0; i < this.accounts.length; i++) {
-      if (accountOrGroupKey(toRemove) === accountOrGroupKey(this.accounts[i])) {
+      if (getUserId(toRemove) === getUserId(this.accounts[i])) {
         this.accounts.splice(i, 1);
         this.reporting.reportInteraction(`Remove from ${this.id}`);
         this.requestUpdate();
@@ -459,7 +453,7 @@
     return difference(
       this.change?.reviewers[this.reviewerState] ?? [],
       this.accounts,
-      (a, b) => accountOrGroupKey(a) === accountOrGroupKey(b)
+      (a, b) => getUserId(a) === getUserId(b)
     );
   }
 }
diff --git a/polygerrit-ui/app/models/bulk-actions/bulk-actions-model.ts b/polygerrit-ui/app/models/bulk-actions/bulk-actions-model.ts
index 57b0158..c40a339 100644
--- a/polygerrit-ui/app/models/bulk-actions/bulk-actions-model.ts
+++ b/polygerrit-ui/app/models/bulk-actions/bulk-actions-model.ts
@@ -23,7 +23,7 @@
   ReviewerInput,
   AttentionSetInput,
 } from '../../types/common';
-import {accountOrGroupKey} from '../../utils/account-util';
+import {getUserId} from '../../utils/account-util';
 
 export const bulkActionsModelToken =
   define<BulkActionsModel>('bulk-actions-model');
@@ -305,7 +305,7 @@
         .get(state)
         ?.filter(account => !change.reviewers[state]?.includes(account))
         .map(account => {
-          return {state, reviewer: accountOrGroupKey(account)};
+          return {state, reviewer: getUserId(account)};
         }) ?? []
     );
   }
diff --git a/polygerrit-ui/app/types/common.ts b/polygerrit-ui/app/types/common.ts
index 67653f3..a21957e 100644
--- a/polygerrit-ui/app/types/common.ts
+++ b/polygerrit-ui/app/types/common.ts
@@ -252,6 +252,8 @@
 // The Encoded UUID of the group
 export type EncodedGroupId = BrandType<string, '_encodedGroupId'>;
 
+export type UserId = AccountId | GroupId | EmailAddress;
+
 // https://gerrit-review.googlesource.com/Documentation/rest-api-accounts.html#contributor-agreement-input
 export interface ContributorAgreementInput {
   name?: string;
@@ -1170,8 +1172,7 @@
  */
 export interface ReviewResult {
   labels?: unknown;
-  // type of key is (AccountId | GroupId | EmailAddress)
-  reviewers?: {[key: string]: AddReviewerResult};
+  reviewers?: {[key: UserId]: AddReviewerResult};
   ready?: boolean;
 }
 
@@ -1182,7 +1183,7 @@
  * TODO(paiking): update this to ReviewerResult while considering removals.
  */
 export interface AddReviewerResult {
-  input: AccountId | GroupId | EmailAddress;
+  input: UserId;
   reviewers?: AccountInfo[];
   ccs?: AccountInfo[];
   error?: string;
@@ -1274,7 +1275,7 @@
  * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#reviewer-input
  */
 export interface ReviewerInput {
-  reviewer: AccountId | GroupId | EmailAddress;
+  reviewer: UserId;
   state?: ReviewerState;
   confirmed?: boolean;
   notify?: NotifyType;
diff --git a/polygerrit-ui/app/utils/account-util.ts b/polygerrit-ui/app/utils/account-util.ts
index d0f53bb..065ba9f 100644
--- a/polygerrit-ui/app/utils/account-util.ts
+++ b/polygerrit-ui/app/utils/account-util.ts
@@ -16,6 +16,7 @@
   NumericChangeId,
   ReviewerInput,
   ServerInfo,
+  UserId,
 } from '../types/common';
 import {AccountTag, ReviewerState} from '../constants/constants';
 import {assertNever, hasOwnProperty} from './common-util';
@@ -52,7 +53,7 @@
   return account?.avatars?.[0]?.url === other?.avatars?.[0]?.url;
 }
 
-export function accountOrGroupKey(entry: AccountInfo | GroupInfo) {
+export function getUserId(entry: AccountInfo | GroupInfo): UserId {
   if (isAccount(entry)) return accountKey(entry);
   if (isGroup(entry)) return entry.id;
   assertNever(entry, 'entry must be account or group');
@@ -65,9 +66,7 @@
 ) {
   if (!change || !state) return false;
   const accounts = [...(change.reviewers[state] ?? [])];
-  return !accounts.some(
-    a => accountOrGroupKey(a) === accountOrGroupKey(account)
-  );
+  return !accounts.some(a => getUserId(a) === getUserId(account));
 }
 
 export function uniqueDefinedAvatar(