Repository: python/mypy
Issue category: Bug Report

Issue:
Generic type aliases with TypeVar defaults infer to an incorrect type
<!--
If you're not sure whether what you're experiencing is a mypy bug, please see the "Question and Help" form instead.
Please consider:

- checking our common issues page: https://mypy.readthedocs.io/en/stable/common_issues.html
- searching our issue tracker: https://github.com/python/mypy/issues to see if it's already been reported
- asking on gitter chat: https://gitter.im/python/typing
-->

**Bug Report**

When using type alias of a generic type which uses type variables with defaults unbound type variables may infer to an incorrect default type.

<!--
If you're reporting a problem with a specific library function, the typeshed tracker is better suited for this report: https://github.com/python/typeshed/issues

If the project you encountered the issue in is open source, please provide a link to the project.
-->

**To Reproduce**

```python
from typing import Generic, TypeVar, TypeAlias

T1 = TypeVar('T1', default=int | None)
T2 = TypeVar('T2', default=int | None)

class A(Generic[T1, T2]):
    def __init__(self, a: T1, b: T2) -> None:
        self.a = a
        self.b = b


MyA: TypeAlias = A[T1, int]
a: MyA = A(None, 10)
```
https://mypy-play.net/?mypy=latest&python=3.12&gist=abece06adfb80e28c6d23bd99997964e

**Expected Behavior**

`MyA` should preserve `T1` default type, which is `int | None`

**Actual Behavior**

`T1` is inferred to `int` default in `MyA`, so assignment to `a` fails with:

`Argument 1 to "A" has incompatible type "None"; expected "int"  [arg-type]`

<!-- What went wrong? Paste mypy's output. -->

**Your Environment**

<!-- Include as many relevant details about the environment you experienced the bug in -->

- Mypy version used: 1.13
- Python version used: 3.12

<!-- You can freely edit this text, please remove all the lines you believe are unnecessary. -->



Hints:

Patch:
diff --git a/mypy/typeanal.py b/mypy/typeanal.py
index daf7ab1951ea..b7e7da17e209 100644
--- a/mypy/typeanal.py
+++ b/mypy/typeanal.py
@@ -2277,7 +2277,8 @@ def set_any_tvars(
         env[tv.id] = arg
     t = TypeAliasType(node, args, newline, newcolumn)
     if not has_type_var_tuple_type:
-        fixed = expand_type(t, env)
+        with state.strict_optional_set(options.strict_optional):
+            fixed = expand_type(t, env)
         assert isinstance(fixed, TypeAliasType)
         t.args = fixed.args
 


Test patch:
diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test
index 3cd94f4a46d2..93d20eb26f6e 100644
--- a/test-data/unit/check-typevar-defaults.test
+++ b/test-data/unit/check-typevar-defaults.test
@@ -728,3 +728,24 @@ class C(Generic[_I]): pass
 
 t: type[C] | int = C
 [builtins fixtures/tuple.pyi]
+
+
+
+[case testGenericTypeAliasWithDefaultTypeVarPreservesNoneInDefault]
+from typing_extensions import TypeVar
+from typing import Generic, Union
+
+T1 = TypeVar("T1", default=Union[int, None])
+T2 = TypeVar("T2", default=Union[int, None])
+
+
+class A(Generic[T1, T2]):
+    def __init__(self, a: T1, b: T2) -> None:
+        self.a = a
+        self.b = b
+
+
+MyA = A[T1, int]
+a: MyA = A(None, 10)
+reveal_type(a.a)  # N: Revealed type is "Union[builtins.int, None]"
+[builtins fixtures/tuple.pyi]