diff --git a/lib/media/media-utils.js b/lib/media/media-utils.js
--- a/lib/media/media-utils.js
+++ b/lib/media/media-utils.js
@@ -32,7 +32,7 @@
     const type = media[0].type.replace('encrypted_', '');
     return `a ${type}`;
   }
-  let firstType;
+  let firstType: ?string;
   for (const single of media) {
     if (!firstType) {
       firstType = single.type;
diff --git a/lib/ops/report-store-ops.test.js b/lib/ops/report-store-ops.test.js
--- a/lib/ops/report-store-ops.test.js
+++ b/lib/ops/report-store-ops.test.js
@@ -1,6 +1,9 @@
 // @flow
 
-import { reportStoreOpsHandlers } from './report-store-ops.js';
+import {
+  type ReportStoreOperation,
+  reportStoreOpsHandlers,
+} from './report-store-ops.js';
 import {
   type ClientReportCreationRequest,
   reportTypes,
@@ -56,7 +59,7 @@
 
 describe('processReportStoreOperations', () => {
   it('should return the original reports if no operations are processed', () => {
-    const reportStoreOps = [];
+    const reportStoreOps: ReportStoreOperation[] = [];
     const processedReports = processReportStoreOperations(
       mockReports,
       reportStoreOps,
diff --git a/lib/reducers/calendar-filters-reducer.js b/lib/reducers/calendar-filters-reducer.js
--- a/lib/reducers/calendar-filters-reducer.js
+++ b/lib/reducers/calendar-filters-reducer.js
@@ -134,7 +134,7 @@
   state: $ReadOnlyArray<CalendarFilter>,
   updateInfos: $ReadOnlyArray<ClientUpdateInfo>,
 ): $ReadOnlyArray<CalendarFilter> {
-  const currentlyFilteredIDs = filteredThreadIDs(state);
+  const currentlyFilteredIDs: ?$ReadOnlySet<string> = filteredThreadIDs(state);
   if (!currentlyFilteredIDs) {
     return state;
   }
diff --git a/lib/reducers/message-reducer.js b/lib/reducers/message-reducer.js
--- a/lib/reducers/message-reducer.js
+++ b/lib/reducers/message-reducer.js
@@ -133,7 +133,7 @@
   );
 }
 
-const newThread = () => ({
+const newThread = (): ThreadMessageInfo => ({
   messageIDs: [],
   startReached: false,
 });
@@ -642,7 +642,7 @@
 
   const filteredThreads: { [string]: ThreadMessageInfo } = {};
   const threadsToRemoveMessagesFrom = [];
-  const messageIDsToRemove = [];
+  const messageIDsToRemove: string[] = [];
   for (const threadID in reassignedMessageStore.threads) {
     const threadMessageInfo = reassignedMessageStore.threads[threadID];
     const threadInfo = threadInfos[threadID];
@@ -950,7 +950,7 @@
       }
     }
 
-    const messageStoreOperations = [
+    const messageStoreOperations: MessageStoreOperation[] = [
       {
         type: 'replace',
         payload: { id: localID, messageInfo: payload },
@@ -1030,7 +1030,7 @@
     };
   } else if (action.type === sendReactionMessageActionTypes.failed) {
     const { localID, threadID } = action.payload;
-    const messageStoreOperations = [];
+    const messageStoreOperations: MessageStoreOperation[] = [];
 
     messageStoreOperations.push({
       type: 'remove',
@@ -1074,7 +1074,7 @@
     const replaceMessageKey = (messageKey: string) =>
       messageKey === payload.localID ? payload.serverID : messageKey;
     let newMessages;
-    const messageStoreOperations = [];
+    const messageStoreOperations: MessageStoreOperation[] = [];
     if (messageStore.messages[payload.serverID]) {
       // If somehow the serverID got in there already, we'll just update the
       // serverID message and scrub the localID one
diff --git a/lib/reducers/thread-reducer.js b/lib/reducers/thread-reducer.js
--- a/lib/reducers/thread-reducer.js
+++ b/lib/reducers/thread-reducer.js
@@ -202,7 +202,7 @@
         threadIDToMostRecentTime.set(messageInfo.threadID, messageInfo.time);
       }
     }
-    const changedThreadInfos = {};
+    const changedThreadInfos: { [string]: RawThreadInfo } = {};
     for (const [threadID, mostRecentTime] of threadIDToMostRecentTime) {
       const threadInfo = state.threadInfos[threadID];
       if (
diff --git a/lib/selectors/calendar-filter-selectors.js b/lib/selectors/calendar-filter-selectors.js
--- a/lib/selectors/calendar-filter-selectors.js
+++ b/lib/selectors/calendar-filter-selectors.js
@@ -12,7 +12,7 @@
 function filteredThreadIDs(
   calendarFilters: $ReadOnlyArray<CalendarFilter>,
 ): ?Set<string> {
-  let threadIDs = [];
+  let threadIDs: string[] = [];
   let threadListFilterExists = false;
   for (const filter of calendarFilters) {
     if (filter.type === calendarThreadFilterTypes.THREAD_LIST) {
diff --git a/lib/selectors/chat-selectors.js b/lib/selectors/chat-selectors.js
--- a/lib/selectors/chat-selectors.js
+++ b/lib/selectors/chat-selectors.js
@@ -159,7 +159,7 @@
   let numReadSidebarsToShow = maxReadSidebars - numUnreadSidebars;
   const threeDaysAgo = Date.now() - threeDays;
 
-  const sidebarItems = [];
+  const sidebarItems: SidebarItem[] = [];
   for (const sidebar of allSidebarItems) {
     if (sidebarItems.length >= maxUnreadSidebars) {
       break;
@@ -414,7 +414,7 @@
     );
   }
 
-  const chatMessageItems = [];
+  const chatMessageItems: ChatMessageItem[] = [];
   let lastMessageInfo = null;
   for (let i = messages.length - 1; i >= 0; i--) {
     const messageInfo = messages[i];
@@ -685,9 +685,9 @@
 
   const additionalMessages = React.useMemo(() => {
     if (!pendingSidebarSourceMessageInfo) {
-      return [];
+      return ([]: MessageInfo[]);
     }
-    const result = [pendingSidebarSourceMessageInfo];
+    const result: MessageInfo[] = [pendingSidebarSourceMessageInfo];
     if (pendingSidebarEditMessageInfo) {
       result.push(pendingSidebarEditMessageInfo);
     }
diff --git a/lib/selectors/user-selectors.js b/lib/selectors/user-selectors.js
--- a/lib/selectors/user-selectors.js
+++ b/lib/selectors/user-selectors.js
@@ -33,7 +33,7 @@
   viewerID: ?string,
   userInfos: UserInfos,
 ): RelativeUserInfo[] {
-  const relativeUserInfos = [];
+  const relativeUserInfos: RelativeUserInfo[] = [];
   for (const userID of userIDs) {
     const username = userInfos[userID] ? userInfos[userID].username : null;
     if (userID === viewerID) {
@@ -53,8 +53,6 @@
   return relativeUserInfos;
 }
 
-const emptyArray = [];
-
 function getRelativeMemberInfos(
   threadInfo: ?RawThreadInfo,
   currentUserID: ?string,
@@ -95,6 +93,8 @@
   return relativeMemberInfos;
 }
 
+const emptyArray: $ReadOnlyArray<RelativeMemberInfo> = [];
+
 // Includes current user at the start
 const baseRelativeMemberInfoSelectorForMembersOfThread = (
   threadID: ?string,
diff --git a/lib/shared/entry-utils.js b/lib/shared/entry-utils.js
--- a/lib/shared/entry-utils.js
+++ b/lib/shared/entry-utils.js
@@ -153,7 +153,7 @@
     return [newCalendarQuery];
   }
 
-  const difference = [];
+  const difference: CalendarQuery[] = [];
   const oldStartDate = dateFromString(oldCalendarQuery.startDate);
   const oldEndDate = dateFromString(oldCalendarQuery.endDate);
   const newStartDate = dateFromString(newCalendarQuery.startDate);
diff --git a/lib/shared/notif-utils.js b/lib/shared/notif-utils.js
--- a/lib/shared/notif-utils.js
+++ b/lib/shared/notif-utils.js
@@ -126,7 +126,10 @@
 
   const prefix = ET`${ET.user({ userInfo: creator })}`;
 
-  let body = `created a new ${threadNoun(threadType, parentThreadInfo.id)}`;
+  let body: string | EntityText = `created a new ${threadNoun(
+    threadType,
+    parentThreadInfo.id,
+  )}`;
   if (parentThreadInfo.name && parentThreadInfo.type !== threadTypes.GENESIS) {
     body = ET`${body} in ${parentThreadInfo.name}`;
   }
@@ -171,7 +174,7 @@
     possessive: true,
   });
 
-  let body = 'started a thread in response to';
+  let body: string | EntityText = 'started a thread in response to';
   body = ET`${body} ${sourceMessageAuthorPossessive} message`;
 
   const { username } = params.notifTargetUserInfo;
diff --git a/lib/shared/radix-tree.js b/lib/shared/radix-tree.js
--- a/lib/shared/radix-tree.js
+++ b/lib/shared/radix-tree.js
@@ -20,7 +20,7 @@
   };
 
   add(key: string, value: V) {
-    let node = this.root;
+    let node: RadixTreeNode<V> = this.root;
     let partLeft = key;
     while (true) {
       partLeft = partLeft.substring(node.part.length);
@@ -92,7 +92,10 @@
 
   getAllMatchingPrefix(prefix: string): V[] {
     const result = new Set<V>();
-    const stack = [{ node: this.root, partLeft: prefix }];
+    const stack: Array<{
+      +node: RadixTreeNode<V>,
+      +partLeft: string,
+    }> = [{ node: this.root, partLeft: prefix }];
     while (stack.length > 0) {
       const { node, partLeft: prevPartLeft } = stack.pop();
       if (node.leaf) {
@@ -133,7 +136,7 @@
   }
 
   getAllMatchingExactly(key: string): V[] {
-    let node = this.root;
+    let node: ?RadixTreeNode<V> = this.root;
     let partLeft = key;
     while (node && partLeft.startsWith(node.part)) {
       if (node.leaf) {
diff --git a/lib/shared/state-sync/entries-state-sync-spec.js b/lib/shared/state-sync/entries-state-sync-spec.js
--- a/lib/shared/state-sync/entries-state-sync-spec.js
+++ b/lib/shared/state-sync/entries-state-sync-spec.js
@@ -85,7 +85,8 @@
   ),
 });
 
-const emptyArray = [];
+const emptyArray: $ReadOnlyArray<ClientEntryInconsistencyReportCreationRequest> =
+  [];
 
 function getEntryInfosHash(
   entryInfos: RawEntryInfos,
diff --git a/lib/shared/state-sync/threads-state-sync-spec.js b/lib/shared/state-sync/threads-state-sync-spec.js
--- a/lib/shared/state-sync/threads-state-sync-spec.js
+++ b/lib/shared/state-sync/threads-state-sync-spec.js
@@ -70,4 +70,5 @@
   ),
 });
 
-const emptyArray = [];
+const emptyArray: $ReadOnlyArray<ClientThreadInconsistencyReportCreationRequest> =
+  [];
diff --git a/lib/shared/state-sync/users-state-sync-spec.js b/lib/shared/state-sync/users-state-sync-spec.js
--- a/lib/shared/state-sync/users-state-sync-spec.js
+++ b/lib/shared/state-sync/users-state-sync-spec.js
@@ -64,4 +64,5 @@
   ),
 });
 
-const emptyArray = [];
+const emptyArray: $ReadOnlyArray<ClientUserInconsistencyReportCreationRequest> =
+  [];
diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js
--- a/lib/shared/thread-utils.js
+++ b/lib/shared/thread-utils.js
@@ -1604,7 +1604,11 @@
     }
   }
 
-  const chatItems = [...privateThreads, ...personalThreads, ...otherThreads];
+  const chatItems: ChatThreadItem[] = [
+    ...privateThreads,
+    ...personalThreads,
+    ...otherThreads,
+  ];
   if (loggedInUserInfo) {
     chatItems.push(
       ...usersSearchResults.map(user =>
@@ -1628,7 +1632,7 @@
   const searchUsers = React.useCallback(
     async (usernamePrefix: string) => {
       if (usernamePrefix.length === 0) {
-        return [];
+        return ([]: GlobalAccountUserInfo[]);
       }
 
       const { userInfos } = await callSearchUsers(usernamePrefix);
@@ -1752,7 +1756,9 @@
   viewerID: string,
 ): ThreadInfo | MinimallyEncodedThreadInfo {
   const members: UserIDAndUsername[] = threadInfo.members
-    .map(({ id, username }) => (username ? { id, username } : null))
+    .map(({ id, username }) =>
+      username ? ({ id, username }: UserIDAndUsername) : null,
+    )
     .filter(Boolean);
   const mentionedNewMembers = extractNewMentionedParentMembers(
     messageText,
diff --git a/lib/socket/activity-handler.react.js b/lib/socket/activity-handler.react.js
--- a/lib/socket/activity-handler.react.js
+++ b/lib/socket/activity-handler.react.js
@@ -10,7 +10,10 @@
 import { connectionSelector } from '../selectors/keyserver-selectors.js';
 import { getMostRecentNonLocalMessageID } from '../shared/message-utils.js';
 import { threadIsPending } from '../shared/thread-utils.js';
-import { queueActivityUpdatesActionType } from '../types/activity-types.js';
+import {
+  queueActivityUpdatesActionType,
+  type ActivityUpdate,
+} from '../types/activity-types.js';
 import type { ConnectionStatus } from '../types/socket-types.js';
 import { useDispatchActionPromise } from '../utils/action-utils.js';
 import { useSelector, useDispatch } from '../utils/redux-utils.js';
@@ -63,7 +66,7 @@
   const callUpdateActivity = useUpdateActivity();
 
   React.useEffect(() => {
-    const activityUpdates = [];
+    const activityUpdates: ActivityUpdate[] = [];
     const isActiveThreadPending = threadIsPending(activeThread);
 
     if (activeThread !== prevActiveThread) {
diff --git a/lib/socket/socket.react.js b/lib/socket/socket.react.js
--- a/lib/socket/socket.react.js
+++ b/lib/socket/socket.react.js
@@ -558,7 +558,7 @@
       this.props.lastCommunicatedPlatformDetails,
     )(getConfig().platformDetails);
 
-    const clientResponses = [];
+    const clientResponses: ClientClientResponse[] = [];
     if (shouldSendInitialPlatformDetails) {
       clientResponses.push({
         type: serverRequestTypes.PLATFORM_DETAILS,
diff --git a/lib/utils/drawer-utils.react.js b/lib/utils/drawer-utils.react.js
--- a/lib/utils/drawer-utils.react.js
+++ b/lib/utils/drawer-utils.react.js
@@ -14,12 +14,15 @@
   ResolvedThreadInfo,
 } from '../types/thread-types.js';
 
-export type CommunityDrawerItemData<T> = {
-  +threadInfo: ThreadInfo | MinimallyEncodedThreadInfo,
-  +itemChildren: $ReadOnlyArray<CommunityDrawerItemData<T>>,
-  +hasSubchannelsButton: boolean,
-  +labelStyle: T,
+type WritableCommunityDrawerItemData<T> = {
+  threadInfo: ThreadInfo | MinimallyEncodedThreadInfo,
+  itemChildren: $ReadOnlyArray<CommunityDrawerItemData<T>>,
+  hasSubchannelsButton: boolean,
+  labelStyle: T,
 };
+export type CommunityDrawerItemData<T> = $ReadOnly<
+  WritableCommunityDrawerItemData<T>,
+>;
 
 function createRecursiveDrawerItemsData<LabelStyleType>(
   childThreadInfosMap: {
@@ -31,7 +34,9 @@
   labelStyles: $ReadOnlyArray<LabelStyleType>,
   maxDepth: number,
 ): $ReadOnlyArray<CommunityDrawerItemData<LabelStyleType>> {
-  const result = communities.map(community => ({
+  const result: $ReadOnlyArray<
+    WritableCommunityDrawerItemData<LabelStyleType>,
+  > = communities.map(community => ({
     threadInfo: community,
     itemChildren: [],
     labelStyle: labelStyles[0],
@@ -79,7 +84,7 @@
 function appendSuffix<
   T: ResolvedThreadInfo | MinimallyEncodedResolvedThreadInfo,
 >(chats: $ReadOnlyArray<T>): T[] {
-  const result = [];
+  const result: T[] = [];
   const names = new Map<string, number>();
 
   for (const chat of chats) {
diff --git a/lib/utils/entity-text.js b/lib/utils/entity-text.js
--- a/lib/utils/entity-text.js
+++ b/lib/utils/entity-text.js
@@ -100,7 +100,7 @@
   strings: $ReadOnlyArray<string>,
   ...entities: $ReadOnlyArray<EntityTextComponent | EntityText>
 ) => {
-  const result = [];
+  const result: EntityTextComponent[] = [];
   for (let i = 0; i < strings.length; i++) {
     const str = strings[i];
     if (str) {
@@ -459,7 +459,7 @@
 function entityTextToObjects(
   entityText: EntityText,
 ): EntityTextComponentAsObject[] {
-  const objs = [];
+  const objs: EntityTextComponentAsObject[] = [];
   for (const entity of entityText) {
     if (typeof entity === 'string') {
       objs.push({ type: 'text', text: entity });
@@ -508,7 +508,7 @@
         entity.display === 'uiName' &&
         typeof entity.uiName !== 'string'
       ) {
-        const uiName = [];
+        const uiName: UserEntity[] = [];
         let changeOccurred = false;
         for (const innerEntity of entity.uiName) {
           if (typeof innerEntity === 'string' || innerEntity.type !== 'user') {
diff --git a/lib/utils/objects.test.js b/lib/utils/objects.test.js
--- a/lib/utils/objects.test.js
+++ b/lib/utils/objects.test.js
@@ -109,7 +109,7 @@
       key1: 'value1',
       key2: 'value2',
     };
-    const map = new Map();
+    const map = new Map<string, string>();
     map.set('value1', 'key1');
     map.set('value2', 'key2');
     expect(invertObjectToMap(obj)).toEqual(map);
@@ -120,7 +120,7 @@
       key1: 1,
       key2: 2,
     };
-    const map = new Map();
+    const map = new Map<number, string>();
     map.set(1, 'key1');
     map.set(2, 'key2');
     expect(invertObjectToMap(obj)).toEqual(map);
@@ -131,7 +131,7 @@
       key1: 1n,
       key2: 2n,
     };
-    const map = new Map();
+    const map = new Map<bigint, string>();
     map.set(1n, 'key1');
     map.set(2n, 'key2');
     expect(invertObjectToMap(obj)).toEqual(map);
@@ -142,7 +142,7 @@
       key1: null,
       key2: null,
     };
-    const map = new Map();
+    const map = new Map<null, string>();
     map.set(null, 'key2');
     expect(() => invertObjectToMap(obj)).toThrowError(
       'invertObjectToMap: obj[key2] is already in invertedMap',