diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index a4289804acea..f8f3b4fcd183 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -2784,6 +2784,9 @@ def overload_arg_similarity(actual: Type, formal: Type) -> int:
 
     The distinction is important in cases where multiple overload items match. We want
     give priority to higher similarity matches.
+
+    If strict-optional is enabled, we count (None -> object) as a promotion, even though it's
+    treated as a subtype elsewhere, to enable overloads between None and object.
     """
     # Replace type variables with their upper bounds. Overloading
     # resolution is based on runtime behavior which erases type
@@ -2809,9 +2812,12 @@ def overload_arg_similarity(actual: Type, formal: Type) -> int:
             # NoneTyp matches anything if we're not doing strict Optional checking
             return 2
         else:
-            # NoneType is a subtype of object
+            # HACK: As a special case, we don't consider NoneType as a subtype of object here, as
+            # otherwise you wouldn't be able to define overloads between object and None. This is
+            # important e.g. for typing descriptors, which get an object when acting as an instance
+            # property and None when acting as a class property.
             if isinstance(formal, Instance) and formal.type.fullname() == "builtins.object":
-                return 2
+                return 1
     if isinstance(actual, UnionType):
         return max(overload_arg_similarity(item, formal)
                    for item in actual.relevant_items())
diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test
index b7f6b047bb02..61fee56e15e1 100644
--- a/test-data/unit/check-optional.test
+++ b/test-data/unit/check-optional.test
@@ -238,6 +238,17 @@ def f(x: int) -> int: pass
 reveal_type(f(None))  # E: Revealed type is 'builtins.str'
 reveal_type(f(0))  # E: Revealed type is 'builtins.int'
 
+[case testOverloadWithObject]
+from foo import *
+[file foo.pyi]
+from typing import overload
+@overload
+def f(x: None) -> str: pass
+@overload
+def f(x: object) -> int: pass
+reveal_type(f(None))  # E: Revealed type is 'builtins.str'
+reveal_type(f(0))  # E: Revealed type is 'builtins.int'
+
 [case testOptionalTypeOrTypePlain]
 from typing import Optional
 def f(a: Optional[int]) -> int: