It seems we can use the following types for hinting LinkedLists in Python:  Union[ListNode, None] or Union['ListNode', None] Optional[ListNode] Optional['ListNode'] ListNode | None ...  Which of these should we prefer? Feel free to share any other relevant insights as well. Attempt Here we simply want to merge K sorted LinkedLists: from typing import Union, List, Any, Optional import unittest import gc class ListNode: def __init__(self, val: int = 0, next: Union['ListNode', None] = None): self.val = val self.next = next class Solution: def mergeKLists(self, linkedlists: Optional[List[ListNode]]) -> Union[ListNode, None]: if not linkedlists: return None interval = 1 while interval < len(linkedlists): for i in range(0, len(linkedlists) - interval, interval << 1): linkedlists[i] = self.merge_two_linkedlists(linkedlists[i], linkedlists[i + interval]) interval <<= 1 return linkedlists[0] def merge_two_linkedlists(self, l1: Union[ListNode, None], l2: Union[ListNode, None]) -> Union[ListNode, None]: sentinel = curr = ListNode() while l1 and l2: if l1.val <= l2.val: curr.next = l1 l1 = l1.next else: curr.next = l2 l2 = l2.next curr = curr.next if l1: curr.next = l1 else: curr.next = l2 return sentinel.next def main_test_merge_k_linkedlists(test_case_class): suite = unittest.TestLoader().loadTestsFromTestCase(test_case_class) runner = unittest.TextTestRunner(verbosity=2, failfast=1) return runner.run(suite) class TestMergeKLists(unittest.TestCase): def test_merge_k_sorted_linkedlists(self): l1 = ListNode(10) l1.next = ListNode(20) l2 = ListNode(40) l2.next = ListNode(50) l3 = ListNode(80) l3.next = ListNode(150) self.assertEqual(Solution().mergeKLists([l1, l2, l3]), l1) l1 = l2 = l3 = None gc.collect() if __name__ == '__main__': main_test_merge_k_linkedlists(TestMergeKLists) 
typing.Optional[T] is just shorthand for typing.Union[T, None]. Would always prefer the former to the latter for its succinctness. Union is of course still useful when using it with something else than None. After Python 3.10, a union of types can simply be written with the | operator (Optional[T] becomes T | None). So nowadays it's unnecessary to import/use either Union or Optional. Also, as of Python 3.9, the built-in collection types support the subscripting [] operator, so a generic typing.List[T] can now just be written as list[T]. The reason for sometimes needing to have a type written in quotes, is that if the type does not exist yet (e.g. using the class as a type within its own body), the string format can still be understood by type checkers and it doesn't cause a runtime NameError. This stringification need can be avoided by using the from __future__ import annotations statement at the top of a module. That statement explicitly opts you into PEP 563 – Postponed Evaluation of Annotations. (That PEP has actually been superseded PEP 649 – Deferred Evaluation Of Annotations Using Descriptors, and it's coming "mandatory" in Python 3.14.)