[ty] Ban NewTypes with generic bases (#22653)

This commit is contained in:
Alex Waygood
2026-01-17 15:23:53 +00:00
committed by GitHub
parent ca57b2595e
commit 2e4774623c
2 changed files with 30 additions and 4 deletions

View File

@@ -541,7 +541,7 @@ class Foo(TypedDict):
Bar = NewType("Bar", Foo) # error: [invalid-newtype]
```
## TODO: A `NewType` cannot be generic
## A `NewType` cannot be generic
```py
from typing import Any, NewType, TypeVar
@@ -553,7 +553,14 @@ B = NewType("B", list[Any])
# But a free typevar is not allowed.
T = TypeVar("T")
C = NewType("C", list[T]) # TODO: should be "error: [invalid-newtype]"
C = NewType("C", list[T]) # error: [invalid-newtype]
D = dict[str, T]
E = NewType("E", D[T]) # error: [invalid-newtype]
# this is fine: omitting the type argument means that this is equivalent
# to `F = NewType("F", dict[str, Any])
F = NewType("F", D)
```
## Forward references in stub files

View File

@@ -6093,7 +6093,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
return error(
&self.context,
format!(
"Wrong number of arguments in `NewType` creation, expected 2, found {}",
"Wrong number of arguments in `NewType` creation: expected 2, found {}",
arguments.args.len()
),
call_expr,
@@ -6183,7 +6183,26 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
fn infer_newtype_assignment_deferred(&mut self, arguments: &ast::Arguments) {
let inferred = self.infer_type_expression(&arguments.args[1]);
match inferred {
Type::NominalInstance(_) | Type::NewTypeInstance(_) => return,
Type::NewTypeInstance(_) => return,
Type::NominalInstance(instance) => {
match instance.class(self.db()) {
ClassType::NonGeneric(_) => {}
ClassType::Generic(alias) => {
let spec = alias.specialization(self.db());
if spec.types(self.db()).iter().any(|t| {
matches!(t, Type::KnownInstance(KnownInstanceType::TypeVar(_)))
}) && let Some(builder) = self
.context
.report_lint(&INVALID_NEWTYPE, &arguments.args[1])
{
let mut diag =
builder.into_diagnostic("invalid base for `typing.NewType`");
diag.set_primary_message("A `NewType` base cannot be generic");
}
}
}
return;
}
// There are exactly two union types allowed as bases for NewType: `int | float` and
// `int | float | complex`. These are allowed because that's what `float` and `complex`
// expand into in type position. We don't currently ask whether the union was implicit