From 2e7ab00d51fae2e6c1fa6842b5dda4dee28b3730 Mon Sep 17 00:00:00 2001 From: David Peter Date: Tue, 4 Nov 2025 16:29:55 +0100 Subject: [PATCH] [ty] Allow values of type `None` in type expressions (#21263) ## Summary Allow values of type `None` in type expressions. The [typing spec](https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions) could be more explicit on whether this is actually allowed or not, but it seems relatively harmless and does help in some use cases like: ```py try: from module import MyClass except ImportError: MyClass = None # ty: ignore def f(m: MyClass): pass ``` ## Test Plan Updated tests, ecosystem check. --- .../resources/mdtest/implicit_type_aliases.md | 5 +---- crates/ty_python_semantic/src/types.rs | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/ty_python_semantic/resources/mdtest/implicit_type_aliases.md b/crates/ty_python_semantic/resources/mdtest/implicit_type_aliases.md index a3e0319f5a..9ce736e235 100644 --- a/crates/ty_python_semantic/resources/mdtest/implicit_type_aliases.md +++ b/crates/ty_python_semantic/resources/mdtest/implicit_type_aliases.md @@ -22,11 +22,8 @@ f(1) ```py MyNone = None -# TODO: this should not be an error -# error: [invalid-type-form] "Variable of type `None` is not allowed in a type expression" def g(x: MyNone): - # TODO: this should be `None` - reveal_type(x) # revealed: Unknown + reveal_type(x) # revealed: None g(None) ``` diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index bee767b763..131d9e7630 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -6600,6 +6600,7 @@ impl<'db> Type<'db> { Type::Dynamic(_) => Ok(*self), Type::NominalInstance(instance) => match instance.known_class(db) { + Some(KnownClass::NoneType) => Ok(Type::none(db)), Some(KnownClass::TypeVar) => Ok(todo_type!( "Support for `typing.TypeVar` instances in type expressions" )),