diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.tsx
similarity index 62%
rename from src/components/AttachmentModal.js
rename to src/components/AttachmentModal.tsx
index 46ce4cf63f26..f3e8ed316c52 100755
--- a/src/components/AttachmentModal.js
+++ b/src/components/AttachmentModal.tsx
@@ -1,32 +1,32 @@
 import Str from 'expensify-common/lib/str';
-import lodashExtend from 'lodash/extend';
-import lodashGet from 'lodash/get';
-import PropTypes from 'prop-types';
 import React, {useCallback, useEffect, useMemo, useState} from 'react';
 import {Animated, Keyboard, View} from 'react-native';
 import {GestureHandlerRootView} from 'react-native-gesture-handler';
 import {withOnyx} from 'react-native-onyx';
-import _ from 'underscore';
+import type {OnyxEntry} from 'react-native-onyx';
 import useLocalize from '@hooks/useLocalize';
 import useNetwork from '@hooks/useNetwork';
 import useStyleUtils from '@hooks/useStyleUtils';
-import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useWindowDimensions from '@hooks/useWindowDimensions';
 import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL';
-import compose from '@libs/compose';
 import fileDownload from '@libs/fileDownload';
 import * as FileUtils from '@libs/fileDownload/FileUtils';
 import Navigation from '@libs/Navigation/Navigation';
 import * as ReportActionsUtils from '@libs/ReportActionsUtils';
 import * as TransactionUtils from '@libs/TransactionUtils';
 import useNativeDriver from '@libs/useNativeDriver';
-import reportPropTypes from '@pages/reportPropTypes';
+import type {AvatarSource} from '@libs/UserUtils';
 import variables from '@styles/variables';
 import * as IOU from '@userActions/IOU';
 import CONST from '@src/CONST';
+import type {TranslationPaths} from '@src/languages/types';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
+import type * as OnyxTypes from '@src/types/onyx';
+import type {EmptyObject} from '@src/types/utils/EmptyObject';
+import {isEmptyObject} from '@src/types/utils/EmptyObject';
+import type ModalType from '@src/types/utils/ModalType';
 import AttachmentCarousel from './Attachments/AttachmentCarousel';
 import AttachmentView from './Attachments/AttachmentView';
 import BlockingView from './BlockingViews/BlockingView';
@@ -37,127 +37,156 @@ import HeaderGap from './HeaderGap';
 import HeaderWithBackButton from './HeaderWithBackButton';
 import * as Expensicons from './Icon/Expensicons';
 import * as Illustrations from './Icon/Illustrations';
-import sourcePropTypes from './Image/sourcePropTypes';
 import Modal from './Modal';
 import SafeAreaConsumer from './SafeAreaConsumer';
-import transactionPropTypes from './transactionPropTypes';
-import withLocalize, {withLocalizePropTypes} from './withLocalize';
-import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions';
 
 /**
  * Modal render prop component that exposes modal launching triggers that can be used
  * to display a full size image or PDF modally with optional confirmation button.
  */
 
-const propTypes = {
+type AttachmentModalOnyxProps = {
+    /** The transaction associated with the receipt attachment, if any */
+    transaction: OnyxEntry<OnyxTypes.Transaction>;
+
+    /** The report associated with the receipt attachment, if any */
+    parentReport: OnyxEntry<OnyxTypes.Report>;
+
+    /** The policy associated with the receipt attachment, if any */
+    policy: OnyxEntry<OnyxTypes.Policy>;
+
+    /** The list of report actions associated with the receipt attachment, if any */
+    parentReportActions: OnyxEntry<OnyxTypes.ReportActions>;
+};
+
+type Attachment = {
+    source: AvatarSource;
+    isAuthTokenRequired: boolean;
+    file: FileObject;
+    isReceipt: boolean;
+    hasBeenFlagged?: boolean;
+    reportActionID?: string;
+};
+
+type ImagePickerResponse = {
+    height: number;
+    name: string;
+    size: number;
+    type: string;
+    uri: string;
+    width: number;
+};
+
+type FileObject = File | ImagePickerResponse;
+
+type ChildrenProps = {
+    displayFileInModal: (data: FileObject) => void;
+    show: () => void;
+};
+
+type AttachmentModalProps = AttachmentModalOnyxProps & {
     /** Optional source (URL, SVG function) for the image shown. If not passed in via props must be specified when modal is opened. */
-    source: PropTypes.oneOfType([PropTypes.string, sourcePropTypes]),
+    source?: AvatarSource;
 
     /** Optional callback to fire when we want to preview an image and approve it for use. */
-    onConfirm: PropTypes.func,
+    onConfirm?: ((file: Partial<FileObject>) => void) | null;
 
     /** Whether the modal should be open by default */
-    defaultOpen: PropTypes.bool,
-
-    /** Optional callback to fire when we want to do something after modal show. */
-    onModalShow: PropTypes.func,
-
-    /** Optional callback to fire when we want to do something after modal hide. */
-    onModalHide: PropTypes.func,
+    defaultOpen?: boolean;
 
     /** Trigger when we explicity click close button in ProfileAttachment modal */
-    onModalClose: PropTypes.func,
-
-    /** Optional callback to fire when we want to do something after attachment carousel changes. */
-    onCarouselAttachmentChange: PropTypes.func,
+    onModalClose?: () => void;
 
     /** Optional original filename when uploading */
-    originalFileName: PropTypes.string,
-
-    /** A function as a child to pass modal launching methods to */
-    children: PropTypes.func,
+    originalFileName?: string;
 
     /** Whether source url requires authentication */
-    isAuthTokenRequired: PropTypes.bool,
+    isAuthTokenRequired?: boolean;
 
     /** Determines if download Button should be shown or not */
-    allowDownload: PropTypes.bool,
+    allowDownload?: boolean;
 
     /** Title shown in the header of the modal */
-    headerTitle: PropTypes.string,
+    headerTitle?: string;
 
     /** The report that has this attachment */
-    report: reportPropTypes,
+    report?: OnyxEntry<OnyxTypes.Report> | EmptyObject;
 
-    /** The transaction associated with the receipt attachment, if any */
-    transaction: transactionPropTypes,
+    /** Optional callback to fire when we want to do something after modal show. */
+    onModalShow?: () => void;
+
+    /** Optional callback to fire when we want to do something after modal hide. */
+    onModalHide?: () => void;
 
     /** The data is loading or not */
-    isLoading: PropTypes.bool,
+    isLoading?: boolean;
 
     /** Should display not found page or not */
-    shouldShowNotFoundPage: PropTypes.bool,
-
-    ...withLocalizePropTypes,
+    shouldShowNotFoundPage?: boolean;
 
-    ...windowDimensionsPropTypes,
+    /** Optional callback to fire when we want to do something after attachment carousel changes. */
+    onCarouselAttachmentChange?: (attachment: Attachment) => void;
 
     /** Denotes whether it is a workspace avatar or not */
-    isWorkspaceAvatar: PropTypes.bool,
+    isWorkspaceAvatar?: boolean;
 
     /** Whether it is a receipt attachment or not */
-    isReceiptAttachment: PropTypes.bool,
+    isReceiptAttachment?: boolean;
 
-    /** Whether the receipt can be replaced */
-    canEditReceipt: PropTypes.bool,
-};
+    /** A function as a child to pass modal launching methods to */
+    children?: React.FC<ChildrenProps>;
 
-const defaultProps = {
-    source: '',
-    onConfirm: null,
-    defaultOpen: false,
-    originalFileName: '',
-    children: null,
-    isAuthTokenRequired: false,
-    allowDownload: false,
-    headerTitle: null,
-    report: {},
-    transaction: {},
-    onModalShow: () => {},
-    onModalHide: () => {},
-    onCarouselAttachmentChange: () => {},
-    isWorkspaceAvatar: false,
-    onModalClose: () => {},
-    isLoading: false,
-    shouldShowNotFoundPage: false,
-    isReceiptAttachment: false,
-    canEditReceipt: false,
+    fallbackSource?: AvatarSource;
+
+    canEditReceipt?: boolean;
 };
 
-function AttachmentModal(props) {
-    const theme = useTheme();
+function AttachmentModal({
+    source = '',
+    onConfirm,
+    defaultOpen = false,
+    originalFileName = '',
+    isAuthTokenRequired = false,
+    allowDownload = false,
+    report,
+    onModalShow = () => {},
+    onModalHide = () => {},
+    onCarouselAttachmentChange = () => {},
+    isReceiptAttachment = false,
+    isWorkspaceAvatar = false,
+    transaction,
+    parentReport,
+    parentReportActions,
+    headerTitle,
+    policy,
+    children,
+    fallbackSource,
+    canEditReceipt = false,
+    onModalClose = () => {},
+    isLoading = false,
+    shouldShowNotFoundPage = false,
+}: AttachmentModalProps) {
     const styles = useThemeStyles();
     const StyleUtils = useStyleUtils();
-    const [isModalOpen, setIsModalOpen] = useState(props.defaultOpen);
+    const [isModalOpen, setIsModalOpen] = useState(defaultOpen);
     const [shouldLoadAttachment, setShouldLoadAttachment] = useState(false);
     const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false);
     const [isDeleteReceiptConfirmModalVisible, setIsDeleteReceiptConfirmModalVisible] = useState(false);
-    const [isAuthTokenRequired, setIsAuthTokenRequired] = useState(props.isAuthTokenRequired);
-    const [attachmentInvalidReasonTitle, setAttachmentInvalidReasonTitle] = useState('');
-    const [attachmentInvalidReason, setAttachmentInvalidReason] = useState(null);
-    const [source, setSource] = useState(() => props.source);
-    const [modalType, setModalType] = useState(CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE);
+    const [isAuthTokenRequiredState, setIsAuthTokenRequiredState] = useState(isAuthTokenRequired);
+    const [attachmentInvalidReasonTitle, setAttachmentInvalidReasonTitle] = useState<TranslationPaths | null>(null);
+    const [attachmentInvalidReason, setAttachmentInvalidReason] = useState<TranslationPaths | null>(null);
+    const [sourceState, setSourceState] = useState(() => source);
+    const [modalType, setModalType] = useState<ModalType>(CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE);
     const [isConfirmButtonDisabled, setIsConfirmButtonDisabled] = useState(false);
     const [confirmButtonFadeAnimation] = useState(() => new Animated.Value(1));
     const [isDownloadButtonReadyToBeShown, setIsDownloadButtonReadyToBeShown] = React.useState(true);
-    const {windowWidth} = useWindowDimensions();
-
-    const isOverlayModalVisible = (props.isReceiptAttachment && isDeleteReceiptConfirmModalVisible) || (!props.isReceiptAttachment && isAttachmentInvalid);
+    const {windowWidth, isSmallScreenWidth} = useWindowDimensions();
+    const isOverlayModalVisible = (isReceiptAttachment && isDeleteReceiptConfirmModalVisible) || (!isReceiptAttachment && isAttachmentInvalid);
 
-    const [file, setFile] = useState(
-        props.originalFileName
+    const [file, setFile] = useState<Partial<FileObject> | undefined>(
+        originalFileName
             ? {
-                  name: props.originalFileName,
+                  name: originalFileName,
               }
             : undefined,
     );
@@ -165,41 +194,35 @@ function AttachmentModal(props) {
     const {isOffline} = useNetwork();
 
     useEffect(() => {
-        setFile(props.originalFileName ? {name: props.originalFileName} : undefined);
-    }, [props.originalFileName]);
-
-    const onCarouselAttachmentChange = props.onCarouselAttachmentChange;
+        setFile(originalFileName ? {name: originalFileName} : undefined);
+    }, [originalFileName]);
 
     /**
      * Keeps the attachment source in sync with the attachment displayed currently in the carousel.
-     * @param {{ source: String, isAuthTokenRequired: Boolean, file: { name: string }, isReceipt: Boolean }} attachment
      */
     const onNavigate = useCallback(
-        (attachment) => {
-            setSource(attachment.source);
+        (attachment: Attachment) => {
+            setSourceState(attachment.source);
             setFile(attachment.file);
-            setIsAuthTokenRequired(attachment.isAuthTokenRequired);
+            setIsAuthTokenRequiredState(attachment.isAuthTokenRequired);
             onCarouselAttachmentChange(attachment);
         },
         [onCarouselAttachmentChange],
     );
 
     /**
-     * If our attachment is a PDF, return the unswipeable Modal type.
-     * @param {String} sourceURL
-     * @param {Object} _file
-     * @returns {String}
+     * If our attachment is a PDF, return the unswipeablge Modal type.
      */
     const getModalType = useCallback(
-        (sourceURL, _file) =>
-            sourceURL && (Str.isPDF(sourceURL) || (_file && Str.isPDF(_file.name || translate('attachmentView.unknownFilename'))))
+        (sourceURL: string, fileObject: FileObject) =>
+            sourceURL && (Str.isPDF(sourceURL) || (fileObject && Str.isPDF(fileObject.name || translate('attachmentView.unknownFilename'))))
                 ? CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE
                 : CONST.MODAL.MODAL_TYPE.CENTERED,
         [translate],
     );
 
     const setDownloadButtonVisibility = useCallback(
-        (isButtonVisible) => {
+        (isButtonVisible: boolean) => {
             if (isDownloadButtonReadyToBeShown === isButtonVisible) {
                 return;
             }
@@ -212,17 +235,19 @@ function AttachmentModal(props) {
      * Download the currently viewed attachment.
      */
     const downloadAttachment = useCallback(() => {
-        let sourceURL = source;
-        if (isAuthTokenRequired) {
+        let sourceURL = sourceState;
+        if (isAuthTokenRequiredState && typeof sourceURL === 'string') {
             sourceURL = addEncryptedAuthTokenToURL(sourceURL);
         }
 
-        fileDownload(sourceURL, lodashGet(file, 'name', ''));
+        if (typeof sourceURL === 'string') {
+            fileDownload(sourceURL, file?.name ?? '');
+        }
 
         // At ios, if the keyboard is open while opening the attachment, then after downloading
         // the attachment keyboard will show up. So, to fix it we need to dismiss the keyboard.
         Keyboard.dismiss();
-    }, [isAuthTokenRequired, source, file]);
+    }, [isAuthTokenRequiredState, sourceState, file]);
 
     /**
      * Execute the onConfirm callback and close the modal.
@@ -234,13 +259,13 @@ function AttachmentModal(props) {
             return;
         }
 
-        if (props.onConfirm) {
-            props.onConfirm(lodashExtend(file, {source}));
+        if (onConfirm) {
+            onConfirm(Object.assign(file ?? {}, {source: sourceState}));
         }
 
         setIsModalOpen(false);
         // eslint-disable-next-line react-hooks/exhaustive-deps
-    }, [isModalOpen, isConfirmButtonDisabled, props.onConfirm, file, source]);
+    }, [isModalOpen, isConfirmButtonDisabled, onConfirm, file, sourceState]);
 
     /**
      * Close the confirm modals.
@@ -254,24 +279,20 @@ function AttachmentModal(props) {
      * Detach the receipt and close the modal.
      */
     const deleteAndCloseModal = useCallback(() => {
-        IOU.detachReceipt(props.transaction.transactionID, props.report.reportID);
+        IOU.detachReceipt(transaction?.transactionID);
         setIsDeleteReceiptConfirmModalVisible(false);
-        Navigation.dismissModal(props.report.reportID);
-    }, [props.transaction, props.report]);
+        Navigation.dismissModal(report?.reportID);
+    }, [transaction, report]);
 
-    /**
-     * @param {Object} _file
-     * @returns {Boolean}
-     */
-    const isValidFile = useCallback((_file) => {
-        if (lodashGet(_file, 'size', 0) > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) {
+    const isValidFile = useCallback((fileObject: FileObject) => {
+        if (fileObject.size > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) {
             setIsAttachmentInvalid(true);
             setAttachmentInvalidReasonTitle('attachmentPicker.attachmentTooLarge');
             setAttachmentInvalidReason('attachmentPicker.sizeExceeded');
             return false;
         }
 
-        if (lodashGet(_file, 'size', 0) < CONST.API_ATTACHMENT_VALIDATIONS.MIN_SIZE) {
+        if (fileObject.size < CONST.API_ATTACHMENT_VALIDATIONS.MIN_SIZE) {
             setIsAttachmentInvalid(true);
             setAttachmentInvalidReasonTitle('attachmentPicker.attachmentTooSmall');
             setAttachmentInvalidReason('attachmentPicker.sizeNotMet');
@@ -280,12 +301,9 @@ function AttachmentModal(props) {
 
         return true;
     }, []);
-    /**
-     * @param {Object} _data
-     * @returns {Boolean}
-     */
-    const isDirectoryCheck = useCallback((_data) => {
-        if (typeof _data.webkitGetAsEntry === 'function' && _data.webkitGetAsEntry().isDirectory) {
+
+    const isDirectoryCheck = useCallback((data: FileObject) => {
+        if ('webkitGetAsEntry' in data && typeof data.webkitGetAsEntry === 'function' && data.webkitGetAsEntry().isDirectory) {
             setIsAttachmentInvalid(true);
             setAttachmentInvalidReasonTitle('attachmentPicker.attachmentError');
             setAttachmentInvalidReason('attachmentPicker.folderNotAllowedMessage');
@@ -294,17 +312,14 @@ function AttachmentModal(props) {
         return true;
     }, []);
 
-    /**
-     * @param {Object} _data
-     */
     const validateAndDisplayFileToUpload = useCallback(
-        (_data) => {
-            if (!isDirectoryCheck(_data)) {
+        (data: FileObject) => {
+            if (!isDirectoryCheck(data)) {
                 return;
             }
-            let fileObject = _data;
-            if (typeof _data.getAsFile === 'function') {
-                fileObject = _data.getAsFile();
+            let fileObject = data;
+            if ('getAsFile' in data && typeof data.getAsFile === 'function') {
+                fileObject = data.getAsFile();
             }
             if (!fileObject) {
                 return;
@@ -327,13 +342,13 @@ function AttachmentModal(props) {
                 const inputSource = URL.createObjectURL(updatedFile);
                 const inputModalType = getModalType(inputSource, updatedFile);
                 setIsModalOpen(true);
-                setSource(inputSource);
+                setSourceState(inputSource);
                 setFile(updatedFile);
                 setModalType(inputModalType);
             } else {
                 const inputModalType = getModalType(fileObject.uri, fileObject);
                 setIsModalOpen(true);
-                setSource(fileObject.uri);
+                setSourceState(fileObject.uri);
                 setFile(fileObject);
                 setModalType(inputModalType);
             }
@@ -347,10 +362,10 @@ function AttachmentModal(props) {
      * we're only updating the opacity of the confirm button, we must also conditionally
      * disable it.
      *
-     * @param {Boolean} shouldFadeOut If true, fade out confirm button. Otherwise fade in.
+     * @param shouldFadeOut If true, fade out confirm button. Otherwise fade in.
      */
     const updateConfirmButtonVisibility = useCallback(
-        (shouldFadeOut) => {
+        (shouldFadeOut: boolean) => {
             setIsConfirmButtonDisabled(shouldFadeOut);
             const toValue = shouldFadeOut ? 0 : 1;
 
@@ -369,12 +384,12 @@ function AttachmentModal(props) {
     const closeModal = useCallback(() => {
         setIsModalOpen(false);
 
-        if (typeof props.onModalClose === 'function') {
-            props.onModalClose();
+        if (typeof onModalClose === 'function') {
+            onModalClose();
         }
 
         // eslint-disable-next-line react-hooks/exhaustive-deps
-    }, [props.onModalClose]);
+    }, [onModalClose]);
 
     /**
      *  open the modal
@@ -384,33 +399,33 @@ function AttachmentModal(props) {
     }, []);
 
     useEffect(() => {
-        setSource(() => props.source);
-    }, [props.source]);
+        setSourceState(() => source);
+    }, [source]);
 
     useEffect(() => {
-        setIsAuthTokenRequired(props.isAuthTokenRequired);
-    }, [props.isAuthTokenRequired]);
+        setIsAuthTokenRequiredState(isAuthTokenRequired);
+    }, [isAuthTokenRequired]);
 
-    const sourceForAttachmentView = source || props.source;
+    const sourceForAttachmentView = sourceState || source;
 
     const threeDotsMenuItems = useMemo(() => {
-        if (!props.isReceiptAttachment || !props.parentReport || !props.parentReportActions) {
+        if (!isReceiptAttachment || !parentReport || !parentReportActions) {
             return [];
         }
 
         const menuItems = [];
-        if (props.canEditReceipt) {
+        if (canEditReceipt) {
             menuItems.push({
                 icon: Expensicons.Camera,
-                text: props.translate('common.replace'),
+                text: translate('common.replace'),
                 onSelected: () => {
                     closeModal();
                     Navigation.navigate(
                         ROUTES.MONEY_REQUEST_STEP_SCAN.getRoute(
                             CONST.IOU.ACTION.EDIT,
                             CONST.IOU.TYPE.REQUEST,
-                            props.transaction.transactionID,
-                            props.report.reportID,
+                            transaction?.transactionID ?? '',
+                            report?.reportID ?? '',
                             Navigation.getActiveRouteWithoutParams(),
                         ),
                     );
@@ -420,14 +435,14 @@ function AttachmentModal(props) {
         if (!isOffline) {
             menuItems.push({
                 icon: Expensicons.Download,
-                text: props.translate('common.download'),
-                onSelected: () => downloadAttachment(source),
+                text: translate('common.download'),
+                onSelected: () => downloadAttachment(),
             });
         }
-        if (TransactionUtils.hasReceipt(props.transaction) && !TransactionUtils.isReceiptBeingScanned(props.transaction) && props.canEditReceipt) {
+        if (TransactionUtils.hasReceipt(transaction) && !TransactionUtils.isReceiptBeingScanned(transaction) && canEditReceipt) {
             menuItems.push({
                 icon: Expensicons.Trashcan,
-                text: props.translate('receipt.deleteReceipt'),
+                text: translate('receipt.deleteReceipt'),
                 onSelected: () => {
                     setIsDeleteReceiptConfirmModalVisible(true);
                 },
@@ -435,17 +450,17 @@ function AttachmentModal(props) {
         }
         return menuItems;
         // eslint-disable-next-line react-hooks/exhaustive-deps
-    }, [props.isReceiptAttachment, props.parentReport, props.parentReportActions, props.policy, props.transaction, file, source]);
+    }, [isReceiptAttachment, parentReport, parentReportActions, policy, transaction, file, sourceState]);
 
     // There are a few things that shouldn't be set until we absolutely know if the file is a receipt or an attachment.
     // props.isReceiptAttachment will be null until its certain what the file is, in which case it will then be true|false.
-    let headerTitle = props.headerTitle;
+    let headerTitleNew = headerTitle;
     let shouldShowDownloadButton = false;
     let shouldShowThreeDotsButton = false;
-    if (!_.isEmpty(props.report)) {
-        headerTitle = translate(props.isReceiptAttachment ? 'common.receipt' : 'common.attachment');
-        shouldShowDownloadButton = props.allowDownload && isDownloadButtonReadyToBeShown && !props.isReceiptAttachment && !isOffline;
-        shouldShowThreeDotsButton = props.isReceiptAttachment && isModalOpen && threeDotsMenuItems.length !== 0;
+    if (!isEmptyObject(report)) {
+        headerTitleNew = translate(isReceiptAttachment ? 'common.receipt' : 'common.attachment');
+        shouldShowDownloadButton = allowDownload && isDownloadButtonReadyToBeShown && !isReceiptAttachment && !isOffline;
+        shouldShowThreeDotsButton = isReceiptAttachment && isModalOpen && threeDotsMenuItems.length !== 0;
     }
 
     return (
@@ -455,26 +470,25 @@ function AttachmentModal(props) {
                 onSubmit={submitAndClose}
                 onClose={isOverlayModalVisible ? closeConfirmModal : closeModal}
                 isVisible={isModalOpen}
-                backgroundColor={theme.componentBG}
                 onModalShow={() => {
-                    props.onModalShow();
+                    onModalShow();
                     setShouldLoadAttachment(true);
                 }}
-                onModalHide={(e) => {
-                    props.onModalHide(e);
+                onModalHide={() => {
+                    onModalHide();
                     setShouldLoadAttachment(false);
                 }}
                 propagateSwipe
             >
                 <GestureHandlerRootView style={styles.flex1}>
-                    {props.isSmallScreenWidth && <HeaderGap />}
+                    {isSmallScreenWidth && <HeaderGap />}
                     <HeaderWithBackButton
-                        title={headerTitle}
+                        title={headerTitleNew}
                         shouldShowBorderBottom
                         shouldShowDownloadButton={shouldShowDownloadButton}
-                        onDownloadButtonPress={() => downloadAttachment(source)}
-                        shouldShowCloseButton={!props.isSmallScreenWidth}
-                        shouldShowBackButton={props.isSmallScreenWidth}
+                        onDownloadButtonPress={() => downloadAttachment()}
+                        shouldShowCloseButton={!isSmallScreenWidth}
+                        shouldShowBackButton={isSmallScreenWidth}
                         onBackButtonPress={closeModal}
                         onCloseButtonPress={closeModal}
                         shouldShowThreeDotsButton={shouldShowThreeDotsButton}
@@ -483,8 +497,8 @@ function AttachmentModal(props) {
                         shouldOverlayDots
                     />
                     <View style={styles.imageModalImageCenterContainer}>
-                        {props.isLoading && <FullScreenLoadingIndicator />}
-                        {props.shouldShowNotFoundPage && !props.isLoading && (
+                        {isLoading && <FullScreenLoadingIndicator />}
+                        {shouldShowNotFoundPage && !isLoading && (
                             <BlockingView
                                 icon={Illustrations.ToddBehindCloud}
                                 iconWidth={variables.modalTopIconWidth}
@@ -496,52 +510,53 @@ function AttachmentModal(props) {
                                 onLinkPress={() => Navigation.dismissModal()}
                             />
                         )}
-                        {!_.isEmpty(props.report) && !props.isReceiptAttachment ? (
+                        {!isEmptyObject(report) && !isReceiptAttachment ? (
                             <AttachmentCarousel
-                                report={props.report}
+                                report={report}
                                 onNavigate={onNavigate}
-                                source={props.source}
+                                source={source}
                                 onToggleKeyboard={updateConfirmButtonVisibility}
                                 setDownloadButtonVisibility={setDownloadButtonVisibility}
                             />
                         ) : (
-                            Boolean(sourceForAttachmentView) &&
+                            !!sourceForAttachmentView &&
                             shouldLoadAttachment &&
-                            !props.isLoading &&
-                            !props.shouldShowNotFoundPage && (
+                            !isLoading &&
+                            !shouldShowNotFoundPage && (
                                 <AttachmentView
+                                    // @ts-expect-error TODO: Remove this once Attachments (https://github.com/Expensify/App/issues/24969) is migrated to TypeScript.
                                     containerStyles={[styles.mh5]}
                                     source={sourceForAttachmentView}
                                     isAuthTokenRequired={isAuthTokenRequired}
                                     file={file}
                                     onToggleKeyboard={updateConfirmButtonVisibility}
-                                    isWorkspaceAvatar={props.isWorkspaceAvatar}
-                                    fallbackSource={props.fallbackSource}
+                                    isWorkspaceAvatar={isWorkspaceAvatar}
+                                    fallbackSource={fallbackSource}
                                     isUsedInAttachmentModal
-                                    transactionID={props.transaction.transactionID}
+                                    transactionID={transaction?.transactionID}
                                 />
                             )
                         )}
                     </View>
                     {/* If we have an onConfirm method show a confirmation button */}
-                    {Boolean(props.onConfirm) && (
+                    {!!onConfirm && (
                         <SafeAreaConsumer>
                             {({safeAreaPaddingBottomStyle}) => (
                                 <Animated.View style={[StyleUtils.fade(confirmButtonFadeAnimation), safeAreaPaddingBottomStyle]}>
                                     <Button
                                         success
-                                        style={[styles.buttonConfirm, props.isSmallScreenWidth ? {} : styles.attachmentButtonBigScreen]}
+                                        style={[styles.buttonConfirm, isSmallScreenWidth ? {} : styles.attachmentButtonBigScreen]}
                                         textStyles={[styles.buttonConfirmText]}
                                         text={translate('common.send')}
                                         onPress={submitAndClose}
-                                        disabled={isConfirmButtonDisabled}
+                                        isDisabled={isConfirmButtonDisabled}
                                         pressOnEnter
                                     />
                                 </Animated.View>
                             )}
                         </SafeAreaConsumer>
                     )}
-                    {props.isReceiptAttachment && (
+                    {isReceiptAttachment && (
                         <ConfirmModal
                             title={translate('receipt.deleteReceipt')}
                             isVisible={isDeleteReceiptConfirmModalVisible}
@@ -555,7 +570,7 @@ function AttachmentModal(props) {
                     )}
                 </GestureHandlerRootView>
             </Modal>
-            {!props.isReceiptAttachment && (
+            {!isReceiptAttachment && (
                 <ConfirmModal
                     title={attachmentInvalidReasonTitle ? translate(attachmentInvalidReasonTitle) : ''}
                     onConfirm={closeConfirmModal}
@@ -567,44 +582,34 @@ function AttachmentModal(props) {
                 />
             )}
 
-            {props.children &&
-                props.children({
-                    displayFileInModal: validateAndDisplayFileToUpload,
-                    show: openModal,
-                })}
+            {children?.({
+                displayFileInModal: validateAndDisplayFileToUpload,
+                show: openModal,
+            })}
         </>
     );
 }
 
-AttachmentModal.propTypes = propTypes;
-AttachmentModal.defaultProps = defaultProps;
 AttachmentModal.displayName = 'AttachmentModal';
-export default compose(
-    withWindowDimensions,
-    withLocalize,
-    withOnyx({
-        transaction: {
-            key: ({report}) => {
-                if (!report) {
-                    return undefined;
-                }
-                const parentReportAction = ReportActionsUtils.getReportAction(report.parentReportID, report.parentReportActionID);
-                const transactionID = lodashGet(parentReportAction, ['originalMessage', 'IOUTransactionID'], 0);
-                return `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`;
-            },
-        },
-        parentReport: {
-            key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report ? report.parentReportID : '0'}`,
-        },
-        policy: {
-            key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`,
-        },
-        parentReportActions: {
-            key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report ? report.parentReportID : '0'}`,
-            canEvict: false,
-        },
-        session: {
-            key: ONYXKEYS.SESSION,
+
+export default withOnyx<AttachmentModalProps, AttachmentModalOnyxProps>({
+    transaction: {
+        key: ({report}) => {
+            const parentReportAction = ReportActionsUtils.getReportAction(report?.parentReportID ?? '', report?.parentReportActionID ?? '');
+            const transactionID = parentReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? parentReportAction?.originalMessage.IOUTransactionID ?? '0' : '0';
+            return `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`;
         },
-    }),
-)(AttachmentModal);
+    },
+    parentReport: {
+        key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report ? report.parentReportID : '0'}`,
+    },
+    policy: {
+        key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`,
+    },
+    parentReportActions: {
+        key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report ? report.parentReportID : '0'}`,
+        canEvict: false,
+    },
+})(AttachmentModal);
+
+export type {Attachment};
diff --git a/src/components/ReportActionItem/ReportActionItemImage.tsx b/src/components/ReportActionItem/ReportActionItemImage.tsx
index 710adeb1589e..e71eb6834cc7 100644
--- a/src/components/ReportActionItem/ReportActionItemImage.tsx
+++ b/src/components/ReportActionItem/ReportActionItemImage.tsx
@@ -1,8 +1,8 @@
 import Str from 'expensify-common/lib/str';
 import React from 'react';
 import type {ReactElement} from 'react';
+import type {ImageSourcePropType, ViewStyle} from 'react-native';
 import {View} from 'react-native';
-import type {ImageSourcePropType} from 'react-native';
 import AttachmentModal from '@components/AttachmentModal';
 import EReceiptThumbnail from '@components/EReceiptThumbnail';
 import Image from '@components/Image';
@@ -79,30 +79,25 @@ function ReportActionItemImage({thumbnail, image, enablePreviewModal = false, tr
         return (
             <ShowContextMenuContext.Consumer>
                 {({report}) => (
-                    // @ts-expect-error TODO: Remove this once AttachmentModal (https://github.com/Expensify/App/issues/25130) is migrated to TypeScript.
                     <AttachmentModal
                         source={imageSource}
                         isAuthTokenRequired={!isLocalFile}
                         report={report}
                         isReceiptAttachment
                         canEditReceipt={canEditReceipt}
-                        allowToDownload
+                        allowDownload
                         originalFileName={transaction?.filename}
                     >
-                        {
-                            // @ts-expect-error TODO: Remove this once AttachmentModal (https://github.com/Expensify/App/issues/25130) is migrated to TypeScript.
-                            ({show}) => (
-                                <PressableWithoutFocus
-                                    // @ts-expect-error TODO: Remove this once AttachmentModal (https://github.com/Expensify/App/issues/25130) is migrated to TypeScript.
-                                    style={[styles.noOutline, styles.w100, styles.h100]}
-                                    onPress={show}
-                                    accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON}
-                                    accessibilityLabel={translate('accessibilityHints.viewAttachment')}
-                                >
-                                    {receiptImageComponent}
-                                </PressableWithoutFocus>
-                            )
-                        }
+                        {({show}) => (
+                            <PressableWithoutFocus
+                                style={[styles.w100, styles.h100, styles.noOutline as ViewStyle]}
+                                onPress={show}
+                                accessibilityRole={CONST.ROLE.BUTTON}
+                                accessibilityLabel={translate('accessibilityHints.viewAttachment')}
+                            >
+                                {receiptImageComponent}
+                            </PressableWithoutFocus>
+                        )}
                     </AttachmentModal>
                 )}
             </ShowContextMenuContext.Consumer>
diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts
index b1a900675949..68085c8d1255 100644
--- a/src/libs/TransactionUtils.ts
+++ b/src/libs/TransactionUtils.ts
@@ -36,10 +36,10 @@ Onyx.connect({
     callback: (value) => (allReports = value),
 });
 
-function isDistanceRequest(transaction: Transaction): boolean {
+function isDistanceRequest(transaction: OnyxEntry<Transaction>): boolean {
     // This is used during the request creation flow before the transaction has been saved to the server
     if (lodashHas(transaction, 'iouRequestType')) {
-        return transaction.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE;
+        return transaction?.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE;
     }
 
     // This is the case for transaction objects once they have been saved to the server
diff --git a/src/pages/ReportAvatar.tsx b/src/pages/ReportAvatar.tsx
index 2a8114da744d..c61a0a748f7d 100644
--- a/src/pages/ReportAvatar.tsx
+++ b/src/pages/ReportAvatar.tsx
@@ -28,7 +28,6 @@ function ReportAvatar({report = {} as Report, policies, isLoadingApp = true}: Re
 
     return (
         <AttachmentModal
-            // @ts-expect-error TODO: Remove this once AttachmentModal (https://github.com/Expensify/App/issues/25130) is migrated to TypeScript.
             headerTitle={policyName}
             defaultOpen
             source={UserUtils.getFullSizeAvatar(avatarURL, 0)}
@@ -38,7 +37,7 @@ function ReportAvatar({report = {} as Report, policies, isLoadingApp = true}: Re
             isWorkspaceAvatar
             originalFileName={policy?.originalFileName ?? policyName}
             shouldShowNotFoundPage={!report?.reportID && !isLoadingApp}
-            isLoading={(!report?.reportID || !policy?.id) && isLoadingApp}
+            isLoading={(!report?.reportID || !policy?.id) && !!isLoadingApp}
         />
     );
 }
diff --git a/src/pages/home/report/ReportAttachments.tsx b/src/pages/home/report/ReportAttachments.tsx
index 42ca51cabe0b..2c6ce7a2dabd 100644
--- a/src/pages/home/report/ReportAttachments.tsx
+++ b/src/pages/home/report/ReportAttachments.tsx
@@ -1,5 +1,6 @@
 import type {StackScreenProps} from '@react-navigation/stack';
 import React, {useCallback} from 'react';
+import type {Attachment} from '@components/AttachmentModal';
 import AttachmentModal from '@components/AttachmentModal';
 import ComposerFocusManager from '@libs/ComposerFocusManager';
 import Navigation from '@libs/Navigation/Navigation';
@@ -8,19 +9,6 @@ import * as ReportUtils from '@libs/ReportUtils';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
 
-type File = {
-    name: string;
-};
-
-type Attachment = {
-    file: File;
-    hasBeenFlagged: boolean;
-    isAuthTokenRequired: boolean;
-    isReceipt: boolean;
-    reportActionID: string;
-    source: string;
-};
-
 type ReportAttachmentsProps = StackScreenProps<AuthScreensParamList, typeof SCREENS.REPORT_ATTACHMENTS>;
 
 function ReportAttachments({route}: ReportAttachmentsProps) {
@@ -33,7 +21,7 @@ function ReportAttachments({route}: ReportAttachmentsProps) {
 
     const onCarouselAttachmentChange = useCallback(
         (attachment: Attachment) => {
-            const routeToNavigate = ROUTES.REPORT_ATTACHMENTS.getRoute(reportID, attachment.source);
+            const routeToNavigate = ROUTES.REPORT_ATTACHMENTS.getRoute(reportID, String(attachment.source));
             Navigation.navigate(routeToNavigate);
         },
         [reportID],
@@ -41,7 +29,6 @@ function ReportAttachments({route}: ReportAttachmentsProps) {
 
     return (
         <AttachmentModal
-            // @ts-expect-error TODO: Remove this once AttachmentModal (https://github.com/Expensify/App/issues/25130) is migrated to TypeScript.
             allowDownload
             defaultOpen
             report={report}
diff --git a/src/pages/settings/Profile/ProfileAvatar.tsx b/src/pages/settings/Profile/ProfileAvatar.tsx
index 2aa0f52609c1..aa39b8eb761f 100644
--- a/src/pages/settings/Profile/ProfileAvatar.tsx
+++ b/src/pages/settings/Profile/ProfileAvatar.tsx
@@ -34,7 +34,6 @@ function ProfileAvatar({route, personalDetails, isLoadingApp = true}: ProfileAva
 
     return (
         <AttachmentModal
-            // @ts-expect-error TODO: Remove this once AttachmentModal (https://github.com/Expensify/App/issues/25130) is migrated to TypeScript.
             headerTitle={personalDetail?.displayName ?? ''}
             defaultOpen
             source={UserUtils.getFullSizeAvatar(avatarURL, accountID)}
@@ -42,7 +41,7 @@ function ProfileAvatar({route, personalDetails, isLoadingApp = true}: ProfileAva
                 Navigation.goBack();
             }}
             originalFileName={personalDetail?.originalFileName ?? ''}
-            isLoading={isLoading}
+            isLoading={!!isLoading}
             shouldShowNotFoundPage={!avatarURL}
         />
     );
diff --git a/src/pages/workspace/WorkspaceAvatar.tsx b/src/pages/workspace/WorkspaceAvatar.tsx
index 1a420ee0fbd3..4215d517878b 100644
--- a/src/pages/workspace/WorkspaceAvatar.tsx
+++ b/src/pages/workspace/WorkspaceAvatar.tsx
@@ -24,7 +24,6 @@ function WorkspaceAvatar({route, policy, isLoadingApp = true}: WorkspaceAvatarPr
 
     return (
         <AttachmentModal
-            // @ts-expect-error TODO: Remove this once AttachmentModal (https://github.com/Expensify/App/issues/25130) is migrated to TypeScript.
             headerTitle={policy?.name ?? ''}
             defaultOpen
             source={UserUtils.getFullSizeAvatar(avatarURL, 0)}
@@ -34,7 +33,7 @@ function WorkspaceAvatar({route, policy, isLoadingApp = true}: WorkspaceAvatarPr
             isWorkspaceAvatar
             originalFileName={policy?.originalFileName ?? policy?.name ?? ''}
             shouldShowNotFoundPage={!Object.keys(policy ?? {}).length && !isLoadingApp}
-            isLoading={!Object.keys(policy ?? {}).length && isLoadingApp}
+            isLoading={!Object.keys(policy ?? {}).length && !!isLoadingApp}
         />
     );
 }
diff --git a/src/styles/utils/generators/ModalStyleUtils.ts b/src/styles/utils/generators/ModalStyleUtils.ts
index 3dfb1e67e038..335ef8382941 100644
--- a/src/styles/utils/generators/ModalStyleUtils.ts
+++ b/src/styles/utils/generators/ModalStyleUtils.ts
@@ -1,9 +1,9 @@
 import type {ViewStyle} from 'react-native';
 import type {ModalProps} from 'react-native-modal';
-import type {ValueOf} from 'type-fest';
 import type {ThemeStyles} from '@styles/index';
 import variables from '@styles/variables';
 import CONST from '@src/CONST';
+import type ModalType from '@src/types/utils/ModalType';
 import type StyleUtilGenerator from './types';
 
 function getCenteredModalStyles(styles: ThemeStyles, windowWidth: number, isSmallScreenWidth: boolean, isFullScreenWhenSmall = false): ViewStyle {
@@ -15,8 +15,6 @@ function getCenteredModalStyles(styles: ThemeStyles, windowWidth: number, isSmal
     };
 }
 
-type ModalType = ValueOf<typeof CONST.MODAL.MODAL_TYPE>;
-
 type WindowDimensions = {
     windowWidth: number;
     windowHeight: number;
diff --git a/src/types/utils/ModalType.ts b/src/types/utils/ModalType.ts
new file mode 100644
index 000000000000..aced9ec35254
--- /dev/null
+++ b/src/types/utils/ModalType.ts
@@ -0,0 +1,6 @@
+import type {ValueOf} from 'type-fest';
+import type CONST from '@src/CONST';
+
+type ModalType = ValueOf<typeof CONST.MODAL.MODAL_TYPE>;
+
+export default ModalType;
