diff --git a/mercurial/merge.py b/mercurial/merge.py
index 10e7adbffa8c800c80ca1514095ad0e54e192afb_bWVyY3VyaWFsL21lcmdlLnB5..f3762eafed66e8d202aecaba4d77c1f74cf55bd7_bWVyY3VyaWFsL21lcmdlLnB5 100644
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -11,7 +11,7 @@
 import os
 import struct
 import typing
-from typing import Dict, Iterator, Optional, Tuple
+from typing import Dict, Iterable, Iterator, Optional, Tuple
 
 from .i18n import _
 from .node import nullrev
@@ -41,6 +41,18 @@
     worker,
 )
 
+if typing.TYPE_CHECKING:
+    # TODO: figure out what exactly is in this tuple
+    MergeResultData = tuple
+    MergeResultAction = tuple[bytes, Optional[MergeResultData], bytes]
+    """The filename, data about the merge, and message about the merge."""
+
+    FileMappingValue = tuple[
+        mergestatemod.MergeAction, Optional[MergeResultData], bytes
+    ]
+    """The merge action, data about the merge, and message about the merge, for
+    the keyed file."""
+
 rust_update_mod = policy.importrust("update")
 
 _pack = struct.pack
@@ -132,7 +144,9 @@
         return None
 
 
-def _checkunknownfiles(repo, wctx, mctx, force, mresult, mergeforce):
+def _checkunknownfiles(
+    repo, wctx, mctx, force, mresult: mergeresult, mergeforce
+):
     """
     Considers any actions that care about the presence of conflicting unknown
     files. For some actions, the result is to abort; for others, it is to
@@ -277,7 +291,7 @@
     )
 
 
-def _forgetremoved(wctx, mctx, branchmerge, mresult):
+def _forgetremoved(wctx, mctx, branchmerge, mresult: mergeresult) -> None:
     """
     Forget removed files
 
@@ -310,7 +324,7 @@
                 )
 
 
-def _checkcollision(repo, wmf, mresult):
+def _checkcollision(repo, wmf, mresult: mergeresult | None) -> None:
     """
     Check for case-folding collisions.
     """
@@ -378,7 +392,7 @@
         lastfull = f
 
 
-def _filesindirs(repo, manifest, dirs):
+def _filesindirs(repo, manifest, dirs) -> Iterator[tuple[bytes, bytes]]:
     """
     Generator that yields pairs of all the files in the manifest that are found
     inside the directories listed in dirs, and which directory they are found
@@ -391,7 +405,7 @@
                 break
 
 
-def checkpathconflicts(repo, wctx, mctx, mresult):
+def checkpathconflicts(repo, wctx, mctx, mresult: mergeresult) -> None:
     """
     Check if any actions introduce path conflicts in the repository, updating
     actions to record or handle the path conflict accordingly.
@@ -492,7 +506,13 @@
         ctxname = bytes(mctx).rstrip(b'+')
         for f, p in _filesindirs(repo, mf, remoteconflicts):
             if f not in deletedfiles:
-                m, args, msg = mresult.getfile(p)
+                mapping_value = mresult.getfile(p)
+
+                # Help pytype- in theory, this could be None since no default
+                # value is passed to getfile() above.
+                assert mapping_value is not None
+
+                m, args, msg = mapping_value
                 pnew = util.safename(p, ctxname, wctx, set(mresult.files()))
                 if m in (
                     mergestatemod.ACTION_DELETED_CHANGED,
@@ -526,7 +546,9 @@
         )
 
 
-def _filternarrowactions(narrowmatch, branchmerge, mresult):
+def _filternarrowactions(
+    narrowmatch, branchmerge, mresult: mergeresult
+) -> None:
     """
     Filters out actions that can ignored because the repo is narrowed.
 
@@ -567,7 +589,12 @@
     It has information about what actions need to be performed on dirstate
     mapping of divergent renames and other such cases."""
 
-    def __init__(self):
+    _filemapping: dict[bytes, FileMappingValue]
+    _actionmapping: dict[
+        mergestatemod.MergeAction, dict[bytes, tuple[MergeResultData, bytes]]
+    ]
+
+    def __init__(self) -> None:
         """
         filemapping: dict of filename as keys and action related info as values
         diverge: mapping of source name -> list of dest name for
@@ -589,7 +616,13 @@
         self._diverge = diverge
         self._renamedelete = renamedelete
 
-    def addfile(self, filename, action, data, message):
+    def addfile(
+        self,
+        filename: bytes,
+        action: mergestatemod.MergeAction,
+        data: MergeResultData | None,
+        message,
+    ) -> None:
         """adds a new file to the mergeresult object
 
         filename: file which we are adding
@@ -606,7 +639,12 @@
         self._filemapping[filename] = (action, data, message)
         self._actionmapping[action][filename] = (data, message)
 
-    def mapaction(self, actionfrom, actionto, transform):
+    def mapaction(
+        self,
+        actionfrom: mergestatemod.MergeAction,
+        actionto: mergestatemod.MergeAction,
+        transform,
+    ):
         """changes all occurrences of action `actionfrom` into `actionto`,
         transforming its args with the function `transform`.
         """
@@ -618,7 +656,9 @@
             self._filemapping[f] = (actionto, data, msg)
             dest[f] = (data, msg)
 
-    def getfile(self, filename, default_return=None):
+    def getfile(
+        self, filename: bytes, default_return: FileMappingValue | None = None
+    ) -> FileMappingValue | None:
         """returns (action, args, msg) about this file
 
         returns default_return if the file is not present"""
@@ -626,7 +666,9 @@
             return self._filemapping[filename]
         return default_return
 
-    def files(self, actions=None):
+    def files(
+        self, actions: Iterable[mergestatemod.MergeAction] | None = None
+    ) -> Iterator[bytes]:
         """returns files on which provided action needs to perfromed
 
         If actions is None, all files are returned
@@ -640,10 +682,10 @@
             for a in actions:
                 yield from self._actionmapping[a]
 
-    def removefile(self, filename):
+    def removefile(self, filename: bytes) -> None:
         """removes a file from the mergeresult object as the file might
         not merging anymore"""
         action, data, message = self._filemapping[filename]
         del self._filemapping[filename]
         del self._actionmapping[action][filename]
 
@@ -644,10 +686,12 @@
         """removes a file from the mergeresult object as the file might
         not merging anymore"""
         action, data, message = self._filemapping[filename]
         del self._filemapping[filename]
         del self._actionmapping[action][filename]
 
-    def getactions(self, actions, sort=False):
+    def getactions(
+        self, actions: Iterable[mergestatemod.MergeAction], sort: bool = False
+    ) -> Iterator[MergeResultAction]:
         """get list of files which are marked with these actions
         if sort is true, files for each action is sorted and then added
 
@@ -662,7 +706,9 @@
                 for f, (args, msg) in self._actionmapping[a].items():
                     yield f, args, msg
 
-    def len(self, actions=None):
+    def len(
+        self, actions: Iterable[mergestatemod.MergeAction] | None = None
+    ) -> int:
         """returns number of files which needs actions
 
         if actions is passed, total of number of files in that action
@@ -673,9 +719,11 @@
 
         return sum(len(self._actionmapping[a]) for a in actions)
 
-    def filemap(self, sort=False) -> Iterator[tuple]:  # TODO: fill out tuple
+    def filemap(
+        self, sort: bool = False
+    ) -> Iterator[tuple[bytes, MergeResultData]]:
         if sort:
             yield from sorted(self._filemapping.items())
         else:
             yield from self._filemapping.items()
 
@@ -677,9 +725,9 @@
         if sort:
             yield from sorted(self._filemapping.items())
         else:
             yield from self._filemapping.items()
 
-    def addcommitinfo(self, filename, key, value):
+    def addcommitinfo(self, filename: bytes, key, value) -> None:
         """adds key-value information about filename which will be required
         while committing this merge"""
         self._commitinfo[filename][key] = value
@@ -697,7 +745,9 @@
         return self._commitinfo
 
     @property
-    def actionsdict(self):
+    def actionsdict(
+        self,
+    ) -> dict[mergestatemod.MergeAction, list[MergeResultAction]]:
         """returns a dictionary of actions to be perfomed with action as key
         and a list of files and related arguments as values"""
         res = collections.defaultdict(list)
@@ -706,9 +756,9 @@
                 res[a].append((f, args, msg))
         return res
 
-    def setactions(self, actions):
+    def setactions(self, actions) -> None:
         self._filemapping = actions
         self._actionmapping = collections.defaultdict(dict)
         for f, (act, data, msg) in self._filemapping.items():
             self._actionmapping[act][f] = data, msg
 
@@ -710,9 +760,9 @@
         self._filemapping = actions
         self._actionmapping = collections.defaultdict(dict)
         for f, (act, data, msg) in self._filemapping.items():
             self._actionmapping[act][f] = data, msg
 
-    def hasconflicts(self):
+    def hasconflicts(self) -> bool:
         """tells whether this merge resulted in some actions which can
         result in conflicts or not"""
         for a in self._actionmapping.keys():
@@ -743,7 +793,7 @@
     acceptremote,
     followcopies,
     forcefulldiff=False,
-):
+) -> mergeresult:
     """
     Merge wctx and p2 with ancestor pa and generate merge action list
 
@@ -1117,7 +1167,7 @@
     return mresult
 
 
-def _resolvetrivial(repo, wctx, mctx, ancestor, mresult):
+def _resolvetrivial(repo, wctx, mctx, ancestor, mresult: mergeresult) -> None:
     """Resolves false conflicts where the nodeid changed but the content
     remained the same."""
     # We force a copy of actions.items() because we're going to mutate
@@ -1146,7 +1196,7 @@
     followcopies,
     matcher=None,
     mergeforce=False,
-):
+) -> mergeresult:
     """
     Calculate the actions needed to merge mctx into wctx using ancestors
 
@@ -1469,7 +1519,7 @@
     yield True, filedata
 
 
-def _prefetchfiles(repo, ctx, mresult):
+def _prefetchfiles(repo, ctx, mresult: mergeresult) -> None:
     """Invoke ``scmutil.prefetchfiles()`` for the files relevant to the dict
     of merge actions.  ``ctx`` is the context being merged in."""
 
@@ -1516,7 +1566,7 @@
 
 def applyupdates(
     repo,
-    mresult,
+    mresult: mergeresult,
     wctx,
     mctx,
     overwrite,
diff --git a/mercurial/sparse.py b/mercurial/sparse.py
index 10e7adbffa8c800c80ca1514095ad0e54e192afb_bWVyY3VyaWFsL3NwYXJzZS5weQ==..f3762eafed66e8d202aecaba4d77c1f74cf55bd7_bWVyY3VyaWFsL3NwYXJzZS5weQ== 100644
--- a/mercurial/sparse.py
+++ b/mercurial/sparse.py
@@ -548,7 +548,7 @@
         elif (old and not new) or (not old and not new and file in dirstate):
             dropped.append(file)
             if file not in pending:
-                mresult.addfile(file, mergestatemod.ACTION_REMOVE, [], b'')
+                mresult.addfile(file, mergestatemod.ACTION_REMOVE, None, b'')
 
     # Verify there are no pending changes in newly included files
     abort = False