@@ -40,27 +40,63 @@ class Foo(Protocol, Protocol): ... # error: [inconsistent-mro]
4040reveal_type(Foo.__mro__ ) # revealed: tuple[Literal[Foo], Unknown, Literal[object]]
4141```
4242
43+ Protocols can also be generic, either by including ` Generic[] ` in the bases list, subscripting
44+ ` Protocol ` directly in the bases list, using PEP-695 type parameters, or some combination of the
45+ above:
46+
47+ ``` py
48+ from typing import TypeVar, Generic
49+
50+ T = TypeVar(" T" )
51+
52+ class Bar0 (Protocol[T]):
53+ x: T
54+
55+ class Bar1 (Protocol[T], Generic[T]):
56+ x: T
57+
58+ class Bar2[T](Protocol):
59+ x: T
60+
61+ class Bar3[T](Protocol[T]):
62+ x: T
63+ ```
64+
65+ It's an error to include both bare ` Protocol ` and subscripted ` Protocol[] ` in the bases list
66+ simultaneously:
67+
68+ ``` py
69+ # TODO : should emit a `[duplicate-bases]` error here:
70+ class DuplicateBases (Protocol , Protocol[T]):
71+ x: T
72+
73+ # TODO : should not have `Generic` multiple times and `Protocol` multiple times
74+ # revealed: tuple[Literal[DuplicateBases], typing.Protocol, typing.Generic, @Todo(`Protocol[]` subscript), @Todo(`Generic[]` subscript), Literal[object]]
75+ reveal_type(DuplicateBases.__mro__ )
76+ ```
77+
4378The introspection helper ` typing(_extensions).is_protocol ` can be used to verify whether a class is
4479a protocol class or not:
4580
4681``` py
4782from typing_extensions import is_protocol
4883
49- # TODO : should be `Literal[True]`
50- reveal_type(is_protocol(MyProtocol)) # revealed: bool
84+ reveal_type(is_protocol(MyProtocol)) # revealed: Literal[True]
85+ reveal_type(is_protocol(Bar0)) # revealed: Literal[True]
86+ reveal_type(is_protocol(Bar1)) # revealed: Literal[True]
87+ reveal_type(is_protocol(Bar2)) # revealed: Literal[True]
88+ reveal_type(is_protocol(Bar3)) # revealed: Literal[True]
5189
5290class NotAProtocol : ...
5391
54- # TODO : should be `Literal[False]`
55- reveal_type(is_protocol(NotAProtocol)) # revealed: bool
92+ reveal_type(is_protocol(NotAProtocol)) # revealed: Literal[False]
5693```
5794
5895A type checker should follow the typeshed stubs if a non-class is passed in, and typeshed's stubs
59- indicate that the argument passed in must be an instance of ` type ` . ` Literal[False] ` should be
60- inferred as the return type, however.
96+ indicate that the argument passed in must be an instance of ` type ` .
6197
6298``` py
63- # TODO : the diagnostic is correct, but should infer `Literal[False]`
99+ # We could also reasonably infer `Literal[False]` here, but it probably doesn't matter that much:
64100# error: [invalid-argument-type]
65101reveal_type(is_protocol(" not a class" )) # revealed: bool
66102```
@@ -74,8 +110,7 @@ class SubclassOfMyProtocol(MyProtocol): ...
74110# revealed: tuple[Literal[SubclassOfMyProtocol], Literal[MyProtocol], typing.Protocol, typing.Generic, Literal[object]]
75111reveal_type(SubclassOfMyProtocol.__mro__ )
76112
77- # TODO : should be `Literal[False]`
78- reveal_type(is_protocol(SubclassOfMyProtocol)) # revealed: bool
113+ reveal_type(is_protocol(SubclassOfMyProtocol)) # revealed: Literal[False]
79114```
80115
81116A protocol class may inherit from other protocols, however, as long as it re-inherits from
@@ -84,8 +119,7 @@ A protocol class may inherit from other protocols, however, as long as it re-inh
84119``` py
85120class SubProtocol (MyProtocol , Protocol ): ...
86121
87- # TODO : should be `Literal[True]`
88- reveal_type(is_protocol(SubProtocol)) # revealed: bool
122+ reveal_type(is_protocol(SubProtocol)) # revealed: Literal[True]
89123
90124class OtherProtocol (Protocol ):
91125 some_attribute: str
@@ -95,8 +129,7 @@ class ComplexInheritance(SubProtocol, OtherProtocol, Protocol): ...
95129# revealed: tuple[Literal[ComplexInheritance], Literal[SubProtocol], Literal[MyProtocol], Literal[OtherProtocol], typing.Protocol, typing.Generic, Literal[object]]
96130reveal_type(ComplexInheritance.__mro__ )
97131
98- # TODO : should be `Literal[True]`
99- reveal_type(is_protocol(ComplexInheritance)) # revealed: bool
132+ reveal_type(is_protocol(ComplexInheritance)) # revealed: Literal[True]
100133```
101134
102135If ` Protocol ` is present in the bases tuple, all other bases in the tuple must be protocol classes,
@@ -134,6 +167,8 @@ reveal_type(Fine.__mro__) # revealed: tuple[Literal[Fine], typing.Protocol, typ
134167
135168class StillFine (Protocol , Generic[T], object ): ...
136169class EvenThis[T](Protocol, object ): ...
170+ class OrThis (Protocol[T], Generic[T]): ...
171+ class AndThis (Protocol[T], Generic[T], object ): ...
137172```
138173
139174And multiple inheritance from a mix of protocol and non-protocol classes is fine as long as
@@ -150,8 +185,7 @@ But if `Protocol` is not present in the bases list, the resulting class doesn't
150185class anymore:
151186
152187``` py
153- # TODO : should reveal `Literal[False]`
154- reveal_type(is_protocol(FineAndDandy)) # revealed: bool
188+ reveal_type(is_protocol(FineAndDandy)) # revealed: Literal[False]
155189```
156190
157191A class does not * have* to inherit from a protocol class in order for it to be considered a subtype
@@ -230,9 +264,10 @@ class Foo(typing.Protocol):
230264class Bar (typing_extensions .Protocol ):
231265 x: int
232266
233- # TODO : these should pass
234- static_assert(typing_extensions.is_protocol(Foo)) # error: [static-assert-error]
235- static_assert(typing_extensions.is_protocol(Bar)) # error: [static-assert-error]
267+ static_assert(typing_extensions.is_protocol(Foo))
268+ static_assert(typing_extensions.is_protocol(Bar))
269+
270+ # TODO : should pass
236271static_assert(is_equivalent_to(Foo, Bar)) # error: [static-assert-error]
237272```
238273
@@ -247,9 +282,10 @@ class RuntimeCheckableFoo(typing.Protocol):
247282class RuntimeCheckableBar (typing_extensions .Protocol ):
248283 x: int
249284
250- # TODO : these should pass
251- static_assert(typing_extensions.is_protocol(RuntimeCheckableFoo)) # error: [static-assert-error]
252- static_assert(typing_extensions.is_protocol(RuntimeCheckableBar)) # error: [static-assert-error]
285+ static_assert(typing_extensions.is_protocol(RuntimeCheckableFoo))
286+ static_assert(typing_extensions.is_protocol(RuntimeCheckableBar))
287+
288+ # TODO : should pass
253289static_assert(is_equivalent_to(RuntimeCheckableFoo, RuntimeCheckableBar)) # error: [static-assert-error]
254290
255291# These should not error because the protocols are decorated with `@runtime_checkable`
0 commit comments