diff --git a/.eslintrc.js b/.eslintrc.js index 1f23ae22ca7e..eb50d1fcc5f3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -100,7 +100,6 @@ module.exports = { __DEV__: 'readonly', }, rules: { - '@typescript-eslint/no-unsafe-argument': 'off', '@typescript-eslint/no-unsafe-call': 'off', '@typescript-eslint/no-unsafe-member-access': 'off', '@typescript-eslint/no-unsafe-assignment': 'off', diff --git a/.github/actions/javascript/bumpVersion/bumpVersion.ts b/.github/actions/javascript/bumpVersion/bumpVersion.ts index ed4828367cf2..eba79c7c9edb 100644 --- a/.github/actions/javascript/bumpVersion/bumpVersion.ts +++ b/.github/actions/javascript/bumpVersion/bumpVersion.ts @@ -1,6 +1,7 @@ import * as core from '@actions/core'; import {exec as originalExec} from 'child_process'; import fs from 'fs'; +import type {PackageJson} from 'type-fest'; import {promisify} from 'util'; import {generateAndroidVersionCode, updateAndroidVersion, updateiOSVersion} from '@github/libs/nativeVersionUpdater'; import * as versionUpdater from '@github/libs/versionUpdater'; @@ -19,7 +20,7 @@ function updateNativeVersions(version: string) { .then(() => { console.log('Successfully updated Android!'); }) - .catch((err) => { + .catch((err: string | Error) => { console.error('Error updating Android'); core.setFailed(err); }); @@ -47,8 +48,12 @@ if (!semanticVersionLevel || !Object.keys(versionUpdater.SEMANTIC_VERSION_LEVELS console.log(`Invalid input for 'SEMVER_LEVEL': ${semanticVersionLevel}`, `Defaulting to: ${semanticVersionLevel}`); } -const {version: previousVersion} = JSON.parse(fs.readFileSync('./package.json').toString()); -const newVersion = versionUpdater.incrementVersion(previousVersion, semanticVersionLevel); +const {version: previousVersion}: PackageJson = JSON.parse(fs.readFileSync('./package.json').toString()); +if (!previousVersion) { + core.setFailed('Error: Could not read package.json'); +} + +const newVersion = versionUpdater.incrementVersion(previousVersion ?? '', semanticVersionLevel); console.log(`Previous version: ${previousVersion}`, `New version: ${newVersion}`); updateNativeVersions(newVersion); diff --git a/.github/actions/javascript/bumpVersion/index.js b/.github/actions/javascript/bumpVersion/index.js index d4a085fc9ddf..e1a5cf13a8d9 100644 --- a/.github/actions/javascript/bumpVersion/index.js +++ b/.github/actions/javascript/bumpVersion/index.js @@ -3478,7 +3478,10 @@ if (!semanticVersionLevel || !Object.keys(versionUpdater.SEMANTIC_VERSION_LEVELS console.log(`Invalid input for 'SEMVER_LEVEL': ${semanticVersionLevel}`, `Defaulting to: ${semanticVersionLevel}`); } const { version: previousVersion } = JSON.parse(fs_1.default.readFileSync('./package.json').toString()); -const newVersion = versionUpdater.incrementVersion(previousVersion, semanticVersionLevel); +if (!previousVersion) { + core.setFailed('Error: Could not read package.json'); +} +const newVersion = versionUpdater.incrementVersion(previousVersion ?? '', semanticVersionLevel); console.log(`Previous version: ${previousVersion}`, `New version: ${newVersion}`); updateNativeVersions(newVersion); console.log(`Setting npm version to ${newVersion}`); diff --git a/.github/actions/javascript/checkDeployBlockers/checkDeployBlockers.ts b/.github/actions/javascript/checkDeployBlockers/checkDeployBlockers.ts index bf94b136ce43..d51d68796070 100644 --- a/.github/actions/javascript/checkDeployBlockers/checkDeployBlockers.ts +++ b/.github/actions/javascript/checkDeployBlockers/checkDeployBlockers.ts @@ -60,7 +60,7 @@ const run = function (): Promise { core.setOutput('HAS_DEPLOY_BLOCKERS', false); } }) - .catch((error) => { + .catch((error: string | Error) => { console.error('A problem occurred while trying to communicate with the GitHub API', error); core.setFailed(error); }); diff --git a/.github/actions/javascript/getGraphiteString/getGraphiteString.ts b/.github/actions/javascript/getGraphiteString/getGraphiteString.ts index 57a941105f90..c486fdbd39f3 100644 --- a/.github/actions/javascript/getGraphiteString/getGraphiteString.ts +++ b/.github/actions/javascript/getGraphiteString/getGraphiteString.ts @@ -28,7 +28,7 @@ const run = () => { // Extract timestamp, Graphite accepts timestamp in seconds if (current.metadata?.creationDate) { - timestamp = Math.floor(new Date(current.metadata.creationDate).getTime() / 1000); + timestamp = Math.floor(new Date(current.metadata.creationDate as string).getTime() / 1000); } if (current.name && current.meanDuration && current.meanCount && timestamp) { diff --git a/.github/actions/javascript/getPreviousVersion/getPreviousVersion.ts b/.github/actions/javascript/getPreviousVersion/getPreviousVersion.ts index dc1e99d1e3b8..262b603124fa 100644 --- a/.github/actions/javascript/getPreviousVersion/getPreviousVersion.ts +++ b/.github/actions/javascript/getPreviousVersion/getPreviousVersion.ts @@ -1,5 +1,6 @@ import * as core from '@actions/core'; import {readFileSync} from 'fs'; +import type {PackageJson} from 'type-fest'; import * as versionUpdater from '@github/libs/versionUpdater'; const semverLevel = core.getInput('SEMVER_LEVEL', {required: true}); @@ -7,6 +8,10 @@ if (!semverLevel || !Object.values(versionUpdater.SEMANTIC_VERSION_LEVEL core.setFailed(`'Error: Invalid input for 'SEMVER_LEVEL': ${semverLevel}`); } -const {version: currentVersion} = JSON.parse(readFileSync('./package.json', 'utf8')); -const previousVersion = versionUpdater.getPreviousVersion(currentVersion, semverLevel); +const {version: currentVersion}: PackageJson = JSON.parse(readFileSync('./package.json', 'utf8')); +if (!currentVersion) { + core.setFailed('Error: Could not read package.json'); +} + +const previousVersion = versionUpdater.getPreviousVersion(currentVersion ?? '', semverLevel); core.setOutput('PREVIOUS_VERSION', previousVersion); diff --git a/.github/actions/javascript/getPreviousVersion/index.js b/.github/actions/javascript/getPreviousVersion/index.js index f372f0fdaf99..8eac2f62f03e 100644 --- a/.github/actions/javascript/getPreviousVersion/index.js +++ b/.github/actions/javascript/getPreviousVersion/index.js @@ -2728,7 +2728,10 @@ if (!semverLevel || !Object.values(versionUpdater.SEMANTIC_VERSION_LEVELS).inclu core.setFailed(`'Error: Invalid input for 'SEMVER_LEVEL': ${semverLevel}`); } const { version: currentVersion } = JSON.parse((0, fs_1.readFileSync)('./package.json', 'utf8')); -const previousVersion = versionUpdater.getPreviousVersion(currentVersion, semverLevel); +if (!currentVersion) { + core.setFailed('Error: Could not read package.json'); +} +const previousVersion = versionUpdater.getPreviousVersion(currentVersion ?? '', semverLevel); core.setOutput('PREVIOUS_VERSION', previousVersion); diff --git a/.github/actions/javascript/reviewerChecklist/reviewerChecklist.ts b/.github/actions/javascript/reviewerChecklist/reviewerChecklist.ts index aabc6b33086a..f57ef6c36a04 100644 --- a/.github/actions/javascript/reviewerChecklist/reviewerChecklist.ts +++ b/.github/actions/javascript/reviewerChecklist/reviewerChecklist.ts @@ -90,7 +90,7 @@ function checkIssueForCompletedChecklist(numberOfChecklistItems: number) { getNumberOfItemsFromReviewerChecklist() .then(checkIssueForCompletedChecklist) - .catch((err) => { + .catch((err: string | Error) => { console.error(err); core.setFailed(err); }); diff --git a/.github/scripts/detectRedirectCycle.ts b/.github/scripts/detectRedirectCycle.ts index 5aa0d1daf342..6da0ecba158c 100644 --- a/.github/scripts/detectRedirectCycle.ts +++ b/.github/scripts/detectRedirectCycle.ts @@ -52,7 +52,7 @@ function detectCycle(): boolean { fs.createReadStream(`${process.cwd()}/docs/redirects.csv`) .pipe(parser) - .on('data', (row) => { + .on('data', (row: [string, string]) => { // Create a directed graph of sourceURL -> targetURL addEdge(row[0], row[1]); }) diff --git a/desktop/contextBridge.ts b/desktop/contextBridge.ts index 487e528a7485..61ede178da2d 100644 --- a/desktop/contextBridge.ts +++ b/desktop/contextBridge.ts @@ -67,7 +67,7 @@ contextBridge.exposeInMainWorld('electron', { } // Deliberately strip event as it includes `sender` - ipcRenderer.on(channel, (event, ...args) => func(...args)); + ipcRenderer.on(channel, (event, ...args: unknown[]) => func(...args)); }, /** Remove listeners for a single channel from the main process and sent to the renderer process. */ diff --git a/desktop/main.ts b/desktop/main.ts index b40557464ec1..64587f42bf56 100644 --- a/desktop/main.ts +++ b/desktop/main.ts @@ -581,7 +581,7 @@ const mainWindow = (): Promise => { app.hide(); } - ipcMain.on(ELECTRON_EVENTS.LOCALE_UPDATED, (event, updatedLocale) => { + ipcMain.on(ELECTRON_EVENTS.LOCALE_UPDATED, (event, updatedLocale: Locale) => { Menu.setApplicationMenu(Menu.buildFromTemplate(localizeMenuItems(initialMenuTemplate, updatedLocale))); disposeContextMenu(); disposeContextMenu = createContextMenu(updatedLocale); @@ -601,7 +601,7 @@ const mainWindow = (): Promise => { // Listen to badge updater event emitted by the render process // and update the app badge count (MacOS only) - ipcMain.on(ELECTRON_EVENTS.REQUEST_UPDATE_BADGE_COUNT, (event, totalCount) => { + ipcMain.on(ELECTRON_EVENTS.REQUEST_UPDATE_BADGE_COUNT, (event, totalCount?: number) => { if (totalCount === -1) { // The electron docs say you should be able to update this and pass no parameters to set the badge // to a single red dot, but in practice it resulted in an error "TypeError: Insufficient number of diff --git a/src/components/AttachmentPicker/index.native.tsx b/src/components/AttachmentPicker/index.native.tsx index ad4cf023c096..218a255d34d3 100644 --- a/src/components/AttachmentPicker/index.native.tsx +++ b/src/components/AttachmentPicker/index.native.tsx @@ -171,7 +171,7 @@ function AttachmentPicker({type = CONST.ATTACHMENT_PICKER_TYPE.FILE, children, s */ const showDocumentPicker = useCallback( (): Promise => - RNDocumentPicker.pick(getDocumentPickerOptions(type)).catch((error) => { + RNDocumentPicker.pick(getDocumentPickerOptions(type)).catch((error: Error) => { if (RNDocumentPicker.isCancel(error)) { return; } @@ -247,7 +247,7 @@ function AttachmentPicker({type = CONST.ATTACHMENT_PICKER_TYPE.FILE, children, s .then((result) => { completeAttachmentSelection.current(result); }) - .catch((error) => { + .catch((error: Error) => { showGeneralAlert(error.message); throw error; }); diff --git a/src/components/Attachments/AttachmentCarousel/index.tsx b/src/components/Attachments/AttachmentCarousel/index.tsx index 3a7e0f19c4cd..42536ae208ae 100644 --- a/src/components/Attachments/AttachmentCarousel/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.tsx @@ -92,13 +92,14 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, return; } + const item: Attachment = entry.item; if (entry.index !== null) { setPage(entry.index); - setActiveSource(entry.item.source); + setActiveSource(item.source); } if (onNavigate) { - onNavigate(entry.item); + onNavigate(item); } }, [isFullScreenRef, onNavigate], diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index f7bf277050a2..bead38512780 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -123,7 +123,7 @@ function Composer( if (shouldCalculateCaretPosition && isRendered) { // we do flushSync to make sure that the valueBeforeCaret is updated before we calculate the caret position to receive a proper position otherwise we will calculate position for the previous state flushSync(() => { - setValueBeforeCaret(webEvent.target.value.slice(0, webEvent.nativeEvent.selection.start)); + setValueBeforeCaret((webEvent.target as HTMLInputElement).value.slice(0, webEvent.nativeEvent.selection.start)); setCaretContent(getNextChars(value ?? '', webEvent.nativeEvent.selection.start)); }); const selectionValue = { diff --git a/src/components/Icon/__mocks__/Expensicons.ts b/src/components/Icon/__mocks__/Expensicons.ts index 78ad86210bf0..e268b109f0a5 100644 --- a/src/components/Icon/__mocks__/Expensicons.ts +++ b/src/components/Icon/__mocks__/Expensicons.ts @@ -1,4 +1,7 @@ -const Expensicons = jest.requireActual('../Expensicons'); +// eslint-disable-next-line import/no-import-module-exports +import type {SvgProps} from 'react-native-svg'; + +const Expensicons = jest.requireActual>>('../Expensicons'); module.exports = Object.keys(Expensicons).reduce((prev, curr) => { // We set the name of the anonymous mock function here so we can dynamically build the list of mocks and access the diff --git a/src/components/OfflineWithFeedback.tsx b/src/components/OfflineWithFeedback.tsx index ba9ce9858d03..1ff3ee2ed737 100644 --- a/src/components/OfflineWithFeedback.tsx +++ b/src/components/OfflineWithFeedback.tsx @@ -9,6 +9,7 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import type {MaybePhraseKey} from '@libs/Localize'; import mapChildrenFlat from '@libs/mapChildrenFlat'; import shouldRenderOffscreen from '@libs/shouldRenderOffscreen'; +import type {AllStyles} from '@styles/utils/types'; import CONST from '@src/CONST'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import type {ReceiptError, ReceiptErrors} from '@src/types/onyx/Transaction'; @@ -111,12 +112,13 @@ function OfflineWithFeedback({ return child; } + const childProps: {children: React.ReactNode | undefined; style: AllStyles} = child.props; const props: StrikethroughProps = { - style: StyleUtils.combineStyles(child.props.style, styles.offlineFeedback.deleted, styles.userSelectNone), + style: StyleUtils.combineStyles(childProps.style, styles.offlineFeedback.deleted, styles.userSelectNone), }; - if (child.props.children) { - props.children = applyStrikeThrough(child.props.children); + if (childProps.children) { + props.children = applyStrikeThrough(childProps.children); } return React.cloneElement(child, props); diff --git a/src/components/Onfido/BaseOnfidoWeb.tsx b/src/components/Onfido/BaseOnfidoWeb.tsx index fefe2ab0a6d1..94124180bea3 100644 --- a/src/components/Onfido/BaseOnfidoWeb.tsx +++ b/src/components/Onfido/BaseOnfidoWeb.tsx @@ -1,4 +1,5 @@ import {Onfido as OnfidoSDK} from 'onfido-sdk-ui'; +import type {ErrorType} from 'onfido-sdk-ui/types/Types'; import React, {forwardRef, useEffect} from 'react'; import type {ForwardedRef} from 'react'; import type {LocaleContextProps} from '@components/LocaleContextProvider'; @@ -92,9 +93,9 @@ function initializeOnfido({sdkToken, onSuccess, onError, onUserExit, preferredLo } onSuccess(data); }, - onError: (error) => { + onError: (error: ErrorType) => { const errorType = error.type; - const errorMessage = error.message ?? CONST.ERROR.UNKNOWN_ERROR; + const errorMessage: string = error.message ?? CONST.ERROR.UNKNOWN_ERROR; Log.hmmm('Onfido error', {errorType, errorMessage}); if (errorType === CONST.WALLET.ERROR.ONFIDO_USER_CONSENT_DENIED) { onUserExit(); diff --git a/src/components/Onfido/index.native.tsx b/src/components/Onfido/index.native.tsx index a7e7a277fff9..10badbcec414 100644 --- a/src/components/Onfido/index.native.tsx +++ b/src/components/Onfido/index.native.tsx @@ -1,4 +1,5 @@ import {OnfidoCaptureType, OnfidoCountryCode, OnfidoDocumentType, Onfido as OnfidoSDK, OnfidoTheme} from '@onfido/react-native-sdk'; +import type {ErrorType} from 'onfido-sdk-ui/types/Types'; import React, {useEffect} from 'react'; import {Alert, Linking} from 'react-native'; import {checkMultiple, PERMISSIONS, RESULTS} from 'react-native-permissions'; @@ -29,15 +30,15 @@ function Onfido({sdkToken, onUserExit, onSuccess, onError}: OnfidoProps) { }, }) .then(onSuccess) - .catch((error) => { - const errorMessage = error.message ?? CONST.ERROR.UNKNOWN_ERROR; + .catch((error: ErrorType) => { + const errorMessage: string = error.message ?? CONST.ERROR.UNKNOWN_ERROR; const errorType = error.type; Log.hmmm('Onfido error on native', {errorType, errorMessage}); // If the user cancels the Onfido flow we won't log this error as it's normal. In the React Native SDK the user exiting the flow will trigger this error which we can use as // our "user exited the flow" callback. On web, this event has it's own callback passed as a config so we don't need to bother with this there. - if ([CONST.ONFIDO.ERROR.USER_CANCELLED, CONST.ONFIDO.ERROR.USER_TAPPED_BACK, CONST.ONFIDO.ERROR.USER_EXITED].includes(errorMessage)) { + if (([CONST.ONFIDO.ERROR.USER_CANCELLED, CONST.ONFIDO.ERROR.USER_TAPPED_BACK, CONST.ONFIDO.ERROR.USER_EXITED] as string[]).includes(errorMessage)) { onUserExit(); return; } diff --git a/src/components/ProfilingToolMenu/BaseProfilingToolMenu.tsx b/src/components/ProfilingToolMenu/BaseProfilingToolMenu.tsx index ff34db881a35..6ab1761fda62 100644 --- a/src/components/ProfilingToolMenu/BaseProfilingToolMenu.tsx +++ b/src/components/ProfilingToolMenu/BaseProfilingToolMenu.tsx @@ -115,7 +115,7 @@ function BaseProfilingToolMenu({isProfilingInProgress = false, pathToBeUsed, dis .then(() => { Log.hmmm('[ProfilingToolMenu] file copied successfully'); }) - .catch((error) => { + .catch((error: Record) => { Log.hmmm('[ProfilingToolMenu] error copying file: ', error); }); diff --git a/src/components/SignInButtons/AppleSignIn/index.android.tsx b/src/components/SignInButtons/AppleSignIn/index.android.tsx index cfd1c48ee8b5..ec669590d029 100644 --- a/src/components/SignInButtons/AppleSignIn/index.android.tsx +++ b/src/components/SignInButtons/AppleSignIn/index.android.tsx @@ -37,11 +37,11 @@ function AppleSignIn() { const handleSignIn = () => { appleSignInRequest() .then((token) => Session.beginAppleSignIn(token)) - .catch((e) => { - if (e.message === appleAuthAndroid.Error.SIGNIN_CANCELLED) { + .catch((error: Record) => { + if (error.message === appleAuthAndroid.Error.SIGNIN_CANCELLED) { return null; } - Log.alert('[Apple Sign In] Apple authentication failed', e); + Log.alert('[Apple Sign In] Apple authentication failed', error); }); }; return ( diff --git a/src/components/SignInButtons/AppleSignIn/index.ios.tsx b/src/components/SignInButtons/AppleSignIn/index.ios.tsx index 3fb1179d0365..4df8375edad8 100644 --- a/src/components/SignInButtons/AppleSignIn/index.ios.tsx +++ b/src/components/SignInButtons/AppleSignIn/index.ios.tsx @@ -1,4 +1,5 @@ import appleAuth from '@invertase/react-native-apple-authentication'; +import type {AppleError} from '@invertase/react-native-apple-authentication'; import React from 'react'; import IconButton from '@components/SignInButtons/IconButton'; import Log from '@libs/Log'; @@ -35,11 +36,11 @@ function AppleSignIn() { const handleSignIn = () => { appleSignInRequest() .then((token) => Session.beginAppleSignIn(token)) - .catch((e) => { - if (e.code === appleAuth.Error.CANCELED) { + .catch((error: {code: AppleError}) => { + if (error.code === appleAuth.Error.CANCELED) { return null; } - Log.alert('[Apple Sign In] Apple authentication failed', e); + Log.alert('[Apple Sign In] Apple authentication failed', error); }); }; return ( diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index 3b189dbb084f..0be7e76a0aa9 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -10,6 +10,8 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {FrequentlyUsedEmoji, Locale} from '@src/types/onyx'; import type {ReportActionReaction, UsersReactions} from '@src/types/onyx/ReportActionReactions'; import type IconAsset from '@src/types/utils/IconAsset'; +import type EmojiTrie from './EmojiTrie'; +import type {SupportedLanguage} from './EmojiTrie'; type HeaderIndice = {code: string; index: number; icon: IconAsset}; type EmojiSpacer = {code: string; spacer: boolean}; @@ -312,9 +314,9 @@ function getAddedEmojis(currentEmojis: Emoji[], formerEmojis: Emoji[]): Emoji[] */ function replaceEmojis(text: string, preferredSkinTone: OnyxEntry = CONST.EMOJI_DEFAULT_SKIN_TONE, lang: Locale = CONST.LOCALES.DEFAULT): ReplacedEmoji { // emojisTrie is importing the emoji JSON file on the app starting and we want to avoid it - const emojisTrie = require('./EmojiTrie').default; + const emojisTrie: typeof EmojiTrie = require('./EmojiTrie').default; - const trie = emojisTrie[lang]; + const trie = emojisTrie[lang as SupportedLanguage]; if (!trie) { return {text, emojis: []}; } @@ -390,9 +392,9 @@ function replaceAndExtractEmojis(text: string, preferredSkinTone: OnyxEntry res.json()) .then((json) => { - const productionVersion = json.tag_name; + const productionVersion: string | semver.SemVer = json.tag_name; if (!productionVersion) { AppUpdate.setIsAppInBeta(false); resolve(false); diff --git a/src/libs/Localize/index.ts b/src/libs/Localize/index.ts index 39a874199004..06fc4fcfc199 100644 --- a/src/libs/Localize/index.ts +++ b/src/libs/Localize/index.ts @@ -67,7 +67,7 @@ type Phrase = TranslationFlatObject[TKey] extends */ const translationCache = new Map, Map>( Object.values(CONST.LOCALES).reduce((cache, locale) => { - cache.push([locale, new Map()]); + cache.push([locale, new Map()]); return cache; }, [] as Array<[ValueOf, Map]>), ); diff --git a/src/libs/Middleware/Logging.ts b/src/libs/Middleware/Logging.ts index 19d767c8a4b1..f10e8d2f5120 100644 --- a/src/libs/Middleware/Logging.ts +++ b/src/libs/Middleware/Logging.ts @@ -1,4 +1,5 @@ import {SIDE_EFFECT_REQUEST_COMMANDS} from '@libs/API/types'; +import type HttpsError from '@libs/Errors/HttpsError'; import Log from '@libs/Log'; import CONST from '@src/CONST'; import type Request from '@src/types/onyx/Request'; @@ -42,7 +43,7 @@ const Logging: Middleware = (response, request) => { logRequestDetails(`Finished API request in ${Date.now() - startTime}ms`, request, data); return data; }) - .catch((error) => { + .catch((error: HttpsError) => { const logParams: Record = { message: error.message, status: error.status, @@ -70,12 +71,12 @@ const Logging: Middleware = (response, request) => { CONST.ERROR.IOS_NETWORK_CONNECTION_LOST_RUSSIAN, CONST.ERROR.IOS_NETWORK_CONNECTION_LOST_SWEDISH, CONST.ERROR.IOS_NETWORK_CONNECTION_LOST_SPANISH, - ].includes(error.message) + ].some((message) => message === error.message) ) { // These errors seem to happen for native devices with interrupted connections. Often we will see logs about Pusher disconnecting together with these. // This type of error may also indicate a problem with SSL certs. Log.hmmm('[Network] API request error: Connection interruption likely', logParams); - } else if ([CONST.ERROR.FIREFOX_DOCUMENT_LOAD_ABORTED, CONST.ERROR.SAFARI_DOCUMENT_LOAD_ABORTED].includes(error.message)) { + } else if ([CONST.ERROR.FIREFOX_DOCUMENT_LOAD_ABORTED, CONST.ERROR.SAFARI_DOCUMENT_LOAD_ABORTED].some((message) => message === error.message)) { // This message can be observed page load is interrupted (closed or navigated away). Log.hmmm('[Network] API request error: User likely navigated away from or closed browser', logParams); } else if (error.message === CONST.ERROR.IOS_LOAD_FAILED) { diff --git a/src/libs/Notification/LocalNotification/BrowserNotifications.ts b/src/libs/Notification/LocalNotification/BrowserNotifications.ts index ee6d00ee2208..18fd8256a5ec 100644 --- a/src/libs/Notification/LocalNotification/BrowserNotifications.ts +++ b/src/libs/Notification/LocalNotification/BrowserNotifications.ts @@ -153,7 +153,7 @@ export default { */ clearNotifications(shouldClearNotification: (notificationData: LocalNotificationData) => boolean) { Object.values(notificationCache) - .filter((notification) => shouldClearNotification(notification.data)) + .filter((notification) => shouldClearNotification(notification.data as LocalNotificationData)) .forEach((notification) => notification.close()); }, }; diff --git a/src/libs/Notification/PushNotification/index.native.ts b/src/libs/Notification/PushNotification/index.native.ts index 426f03c6d408..34699f0610e1 100644 --- a/src/libs/Notification/PushNotification/index.native.ts +++ b/src/libs/Notification/PushNotification/index.native.ts @@ -130,7 +130,7 @@ const register: Register = (notificationID) => { // Refresh notification opt-in status NVP for the new user. refreshNotificationOptInStatus(); }) - .catch((error) => { + .catch((error: Record) => { Log.warn('[PushNotification] Failed to register for push notifications! Reason: ', error); }); }; diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index 48c5e5c1409f..d4e17846d412 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -36,7 +36,9 @@ type PusherEventMap = { [TYPE.USER_IS_LEAVING_ROOM]: UserIsLeavingRoomEvent; }; -type EventData = EventName extends keyof PusherEventMap ? PusherEventMap[EventName] : OnyxUpdatesFromServer; +type EventData = {chunk?: string; id?: string; index?: number; final?: boolean} & (EventName extends keyof PusherEventMap + ? PusherEventMap[EventName] + : OnyxUpdatesFromServer); type EventCallbackError = {type: ValueOf; data: {code: number}}; @@ -164,7 +166,7 @@ function bindEventToChannel(channel: Channel return; } - let data; + let data: EventData; try { data = isObject(eventData) ? eventData : JSON.parse(eventData); } catch (err) { @@ -187,7 +189,9 @@ function bindEventToChannel(channel: Channel // Add it to the rolling list. const chunkedEvent = chunkedDataEvents[data.id]; - chunkedEvent.chunks[data.index] = data.chunk; + if (data.index !== undefined) { + chunkedEvent.chunks[data.index] = data.chunk; + } // If this is the last packet, mark that we've hit the end. if (data.final) { @@ -198,7 +202,7 @@ function bindEventToChannel(channel: Channel // packet. if (chunkedEvent.receivedFinal && chunkedEvent.chunks.length === Object.keys(chunkedEvent.chunks).length) { try { - eventCallback(JSON.parse(chunkedEvent.chunks.join(''))); + eventCallback(JSON.parse(chunkedEvent.chunks.join('')) as EventData); } catch (err) { Log.alert('[Pusher] Unable to parse chunked JSON response from Pusher', { error: err, diff --git a/src/libs/actions/Device/index.ts b/src/libs/actions/Device/index.ts index e7c19d20e4fe..ed700c06a97f 100644 --- a/src/libs/actions/Device/index.ts +++ b/src/libs/actions/Device/index.ts @@ -43,7 +43,7 @@ function setDeviceID() { Log.info('Got new deviceID', false, uniqueID); Onyx.set(ONYXKEYS.DEVICE_ID, uniqueID); }) - .catch((err) => Log.info('Found existing deviceID', false, err.message)); + .catch((error: Error) => Log.info('Found existing deviceID', false, error.message)); } /** diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index 105eadffd436..fe415b137fe6 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -366,7 +366,7 @@ const ContextMenuActions: ContextMenuAction[] = [ Clipboard.setString(Localize.translateLocal('iou.unheldExpense')); } else if (content) { setClipboardMessage( - content.replace(/()(.*?)(<\/mention-user>)/gi, (match, openTag, innerContent, closeTag): string => { + content.replace(/()(.*?)(<\/mention-user>)/gi, (match, openTag: string, innerContent: string, closeTag: string): string => { const modifiedContent = Str.removeSMSDomain(innerContent) || ''; return openTag + modifiedContent + closeTag || ''; }), diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 29b25828d24b..0b585de8f059 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -177,6 +177,11 @@ type ComposerWithSuggestionsProps = ComposerWithSuggestionsOnyxProps & policyID: string; }; +type SwitchToCurrentReportProps = { + preexistingReportID: string; + callback: () => void; +}; + const {RNTextInputReset} = NativeModules; const isIOSNative = getPlatform() === CONST.PLATFORM.IOS; @@ -336,7 +341,7 @@ function ComposerWithSuggestions( const debouncedSaveReportComment = useMemo( () => - lodashDebounce((selectedReportID, newComment) => { + lodashDebounce((selectedReportID: string, newComment: string | null) => { Report.saveReportDraftComment(selectedReportID, newComment); isCommentPendingSaved.current = false; }, 1000), @@ -344,7 +349,7 @@ function ComposerWithSuggestions( ); useEffect(() => { - const switchToCurrentReport = DeviceEventEmitter.addListener(`switchToPreExistingReport_${reportID}`, ({preexistingReportID, callback}) => { + const switchToCurrentReport = DeviceEventEmitter.addListener(`switchToPreExistingReport_${reportID}`, ({preexistingReportID, callback}: SwitchToCurrentReportProps) => { if (!commentRef.current) { callback(); return; diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index d8aed24e3b13..bdc0b80375fa 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -303,12 +303,12 @@ function ReportActionsList({ setCurrentUnreadMarker(null); }; - const unreadActionSubscription = DeviceEventEmitter.addListener(`unreadAction_${report.reportID}`, (newLastReadTime) => { + const unreadActionSubscription = DeviceEventEmitter.addListener(`unreadAction_${report.reportID}`, (newLastReadTime: string) => { resetUnreadMarker(newLastReadTime); setMessageManuallyMarkedUnread(new Date().getTime()); }); - const readNewestActionSubscription = DeviceEventEmitter.addListener(`readNewestAction_${report.reportID}`, (newLastReadTime) => { + const readNewestActionSubscription = DeviceEventEmitter.addListener(`readNewestAction_${report.reportID}`, (newLastReadTime: string) => { resetUnreadMarker(newLastReadTime); setMessageManuallyMarkedUnread(0); }); diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index c022a079df65..b844aa29f670 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -105,11 +105,11 @@ function IOURequestStepScan({ return; } - camera.current.focus(point).catch((ex) => { - if (ex.message === '[unknown/unknown] Cancelled by another startFocusAndMetering()') { + camera.current.focus(point).catch((error: Record) => { + if (error.message === '[unknown/unknown] Cancelled by another startFocusAndMetering()') { return; } - Log.warn('Error focusing camera', ex); + Log.warn('Error focusing camera', error); }); }; diff --git a/tests/e2e/server/index.ts b/tests/e2e/server/index.ts index 51b73447bab4..52e50fefc0ae 100644 --- a/tests/e2e/server/index.ts +++ b/tests/e2e/server/index.ts @@ -153,7 +153,7 @@ const createServerInstance = (): ServerInstance => { res.statusCode = 500; res.end('Error executing command'); }) - .catch((error) => { + .catch((error: string) => { Logger.error('Error executing command', error); res.statusCode = 500; res.end('Error executing command'); diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index e5c7e0359eed..0f13062b2e94 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -5,6 +5,7 @@ import {addSeconds, format, subMinutes, subSeconds} from 'date-fns'; import {utcToZonedTime} from 'date-fns-tz'; import React from 'react'; import {AppState, DeviceEventEmitter, Linking} from 'react-native'; +import type {ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type Animated from 'react-native-reanimated'; @@ -77,7 +78,7 @@ const createAddListenerMock = (): ListenerMock => { transitionEndListeners.forEach((transitionEndListener) => transitionEndListener()); }; - const addListener: jest.Mock = jest.fn().mockImplementation((listener, callback) => { + const addListener: jest.Mock = jest.fn().mockImplementation((listener, callback: () => void) => { if (listener === 'transitionEnd') { transitionEndListeners.push(callback); } @@ -157,7 +158,10 @@ function scrollUpToRevealNewMessagesBadge() { function isNewMessagesBadgeVisible(): boolean { const hintText = Localize.translateLocal('accessibilityHints.scrollToNewestMessages'); const badge = screen.queryByAccessibilityHint(hintText); - return Math.round(badge?.props.style.transform[0].translateY) === -40; + const badgeProps = badge?.props as {style: ViewStyle}; + const transformStyle = badgeProps.style.transform?.[0] as {translateY: number}; + + return Math.round(transformStyle.translateY) === -40; } function navigateToSidebar(): Promise { diff --git a/tests/unit/MiddlewareTest.ts b/tests/unit/MiddlewareTest.ts index cc4bf7d0df6b..6954b22b2706 100644 --- a/tests/unit/MiddlewareTest.ts +++ b/tests/unit/MiddlewareTest.ts @@ -49,9 +49,9 @@ describe('Middleware', () => { expect(global.fetch).toHaveBeenCalledTimes(2); expect(global.fetch).toHaveBeenLastCalledWith('https://www.expensify.com.dev/api/AddComment?', expect.anything()); - TestHelper.assertFormDataMatchesObject((global.fetch as jest.Mock).mock.calls[1][1].body, {reportID: '1234', reportActionID: '5678'}); + TestHelper.assertFormDataMatchesObject((global.fetch as jest.Mock).mock.calls[1][1].body as TestHelper.FormData, {reportID: '1234', reportActionID: '5678'}); expect(global.fetch).toHaveBeenNthCalledWith(1, 'https://www.expensify.com.dev/api/OpenReport?', expect.anything()); - TestHelper.assertFormDataMatchesObject((global.fetch as jest.Mock).mock.calls[0][1].body, {reportID: '1234'}); + TestHelper.assertFormDataMatchesObject((global.fetch as jest.Mock).mock.calls[0][1].body as TestHelper.FormData, {reportID: '1234'}); }); test('Request with preexistingReportID', async () => { @@ -93,9 +93,9 @@ describe('Middleware', () => { expect(global.fetch).toHaveBeenCalledTimes(2); expect(global.fetch).toHaveBeenLastCalledWith('https://www.expensify.com.dev/api/AddComment?', expect.anything()); - TestHelper.assertFormDataMatchesObject((global.fetch as jest.Mock).mock.calls[1][1].body, {reportID: '5555', reportActionID: '5678'}); + TestHelper.assertFormDataMatchesObject((global.fetch as jest.Mock).mock.calls[1][1].body as TestHelper.FormData, {reportID: '5555', reportActionID: '5678'}); expect(global.fetch).toHaveBeenNthCalledWith(1, 'https://www.expensify.com.dev/api/OpenReport?', expect.anything()); - TestHelper.assertFormDataMatchesObject((global.fetch as jest.Mock).mock.calls[0][1].body, {reportID: '1234'}); + TestHelper.assertFormDataMatchesObject((global.fetch as jest.Mock).mock.calls[0][1].body as TestHelper.FormData, {reportID: '1234'}); }); }); }); diff --git a/tests/unit/markPullRequestsAsDeployedTest.ts b/tests/unit/markPullRequestsAsDeployedTest.ts index d533435273d4..24a6733d1244 100644 --- a/tests/unit/markPullRequestsAsDeployedTest.ts +++ b/tests/unit/markPullRequestsAsDeployedTest.ts @@ -196,7 +196,7 @@ platform | result }); it('comments on pull requests correctly for a standard production deploy', async () => { - mockGetInput.mockImplementation((key) => { + mockGetInput.mockImplementation((key: string) => { if (key === 'IS_PRODUCTION_DEPLOY') { return true; } @@ -226,7 +226,7 @@ platform | result }); it('comments on pull requests correctly for a cherry pick', async () => { - mockGetInput.mockImplementation((key) => { + mockGetInput.mockImplementation((key: string) => { if (key === 'PR_LIST') { return JSON.stringify([3]); } @@ -281,7 +281,7 @@ platform | result }); it('comments on pull requests correctly when one platform fails', async () => { - mockGetInput.mockImplementation((key) => { + mockGetInput.mockImplementation((key: string) => { if (key === 'ANDROID') { return 'skipped'; } diff --git a/tests/utils/TestHelper.ts b/tests/utils/TestHelper.ts index bd107ba6ed56..11befebea340 100644 --- a/tests/utils/TestHelper.ts +++ b/tests/utils/TestHelper.ts @@ -230,7 +230,7 @@ const createAddListenerMock = () => { transitionEndListeners.forEach((transitionEndListener) => transitionEndListener()); }; - const addListener = jest.fn().mockImplementation((listener, callback) => { + const addListener = jest.fn().mockImplementation((listener, callback: Listener) => { if (listener === 'transitionEnd') { transitionEndListeners.push(callback); } @@ -242,5 +242,5 @@ const createAddListenerMock = () => { return {triggerTransitionEnd, addListener}; }; -export type {MockFetch}; +export type {MockFetch, FormData}; export {assertFormDataMatchesObject, buildPersonalDetails, buildTestReportComment, createAddListenerMock, getGlobalFetchMock, setPersonalDetails, signInWithTestUser, signOutTestUser}; diff --git a/tests/utils/collections/reportActions.ts b/tests/utils/collections/reportActions.ts index 65cbb3ba966e..152358cedb49 100644 --- a/tests/utils/collections/reportActions.ts +++ b/tests/utils/collections/reportActions.ts @@ -2,17 +2,16 @@ import {rand, randAggregation, randBoolean, randWord} from '@ngneat/falso'; import {format} from 'date-fns'; import CONST from '@src/CONST'; import type {ReportAction} from '@src/types/onyx'; +import type {ActionName} from '@src/types/onyx/OriginalMessage'; +import type DeepRecord from '@src/types/utils/DeepRecord'; -type ActionType = keyof typeof CONST.REPORT.ACTIONS.TYPE; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const flattenActionNamesValues = (actionNames: any) => { - let result = [] as ActionType[]; - Object.keys(actionNames).forEach((key) => { - if (typeof actionNames[key] === 'object') { - result = result.concat(flattenActionNamesValues(actionNames[key])); +const flattenActionNamesValues = (actionNames: DeepRecord) => { + let result: ActionName[] = []; + Object.values(actionNames).forEach((value) => { + if (typeof value === 'object') { + result = result.concat(flattenActionNamesValues(value)); } else { - result.push(actionNames[key]); + result.push(value); } }); return result; diff --git a/workflow_tests/utils/ExtendedAct.ts b/workflow_tests/utils/ExtendedAct.ts index b0f4d7e159e4..e2bb12ec8e01 100644 --- a/workflow_tests/utils/ExtendedAct.ts +++ b/workflow_tests/utils/ExtendedAct.ts @@ -42,14 +42,14 @@ class ExtendedAct extends Act { if (opts.workflowFile) { workflowFiles = [path.basename(opts.workflowFile)]; } else if (this['workflowFile'] !== this['cwd']) { - workflowFiles = [path.basename(this['workflowFile'])]; + workflowFiles = [path.basename(this['workflowFile'] as string)]; } else { const availableWorkflows = await this.list(undefined, opts.cwd, opts.workflowFile); workflowFiles = availableWorkflows.filter(filter).map((workflow: Workflow) => workflow.workflowFile); } return workflowFiles.map((workflowFile) => { - const jobMocker = new JobMocker(workflowFile, opts.cwd ?? this['cwd']); + const jobMocker = new JobMocker(workflowFile, opts.cwd ?? (this['cwd'] as string)); return jobMocker.mock(opts.mockJobs); }); } diff --git a/workflow_tests/utils/preGenerateTest.ts b/workflow_tests/utils/preGenerateTest.ts index 25bdb8f00ae3..1e7e7bb04184 100644 --- a/workflow_tests/utils/preGenerateTest.ts +++ b/workflow_tests/utils/preGenerateTest.ts @@ -7,6 +7,8 @@ import {exit} from 'process'; import yaml from 'yaml'; import type {YamlMockJob, YamlWorkflow} from './JobMocker'; +type Step = {name: string; with?: string; envs?: string[]; inputs?: string[]}; + const workflowsDirectory = path.resolve(__dirname, '..', '..', '.github', 'workflows'); const workflowTestsDirectory = path.resolve(__dirname, '..'); const workflowTestMocksDirectory = path.join(workflowTestsDirectory, 'mocks'); @@ -199,7 +201,7 @@ const parseWorkflowFile = (workflow: YamlWorkflow) => { workflowJobs[jobId] = { steps: [], }; - job.steps.forEach((step) => { + job.steps.forEach((step: Step) => { const workflowStep = { name: step.name, inputs: Object.keys(step.with ?? {}), @@ -243,7 +245,7 @@ const getAssertionsFileContent = (jobs: Record): string => Object.entries(jobs).forEach(([jobId, job]) => { let stepAssertionsContent = ''; - job.steps.forEach((step) => { + job.steps.forEach((step: Step) => { stepAssertionsContent += stepAssertionTemplate(step.name, jobId.toUpperCase(), step.name, step.inputs, step.envs); }); const jobAssertionName = `assert${jobId.charAt(0).toUpperCase() + jobId.slice(1)}JobExecuted`; @@ -275,7 +277,7 @@ checkIfMocksFileExists(workflowTestMocksDirectory, workflowTestMocksFileName); const workflowTestAssertionsFileName = `${workflowName}Assertions.ts`; checkIfAssertionsFileExists(workflowTestAssertionsDirectory, workflowTestAssertionsFileName); -const workflow = yaml.parse(fs.readFileSync(workflowFilePath, 'utf8')); +const workflow: YamlWorkflow = yaml.parse(fs.readFileSync(workflowFilePath, 'utf8')); const workflowJobs = parseWorkflowFile(workflow); const mockFileContent = getMockFileContent(workflowName, workflowJobs);