Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 747 – TypeExpr: Type Hint for a Type Expression

Author:
David Foster <david at dafoster.net>
Sponsor:
Jelle Zijlstra <jelle.zijlstra at gmail >
Discussions-To:
Discourse thread
Status:
Draft
Type:
Standards Track
Topic:
Typing
Created:
27-May-2024
Python-Version:
3.14
Post-History:
19-Apr-2024,04-May-2024,17-Jun-2024

Table of Contents

Abstract

PEP 484defines the notationtype[C]whereCis a class, to refer to a class object that is a subtype ofC.It explicitly does not allowtype[C]to refer to arbitrary type expressionobjects such as the runtime objectstr|None,even ifCis an unbounded TypeVar.[1]In cases where that restriction is unwanted, this PEP proposes a new notationTypeExpr[T]whereTis a type, to refer to a either a class object or some other type expression object that is a subtype ofT,allowing any kind of type to be referenced.

This PEP makes no Python grammar changes. Correct usage of TypeExpr[]is intended to be enforced only by static and runtime type checkers and need not be enforced by Python itself at runtime.

Motivation

The introduction ofTypeExprallows new kinds of metaprogramming functions that operate on type expressions to be type-annotated and understood by type checkers.

For example, here is a function that checks whether a value is assignable to a variable of a particular type, and if so returns the original value:

deftrycast[T](typx:TypeExpr[T],value:object)->T|None:...

The use ofTypeExpr[]and the type variableTenables the return type of this function to be influenced by atypxvalue passed at runtime, which is quite powerful.

Here is another function that checks whether a value is assignable to a variable of a particular type, and if so returnsTrue(as a special TypeIs[]bool[2]):

defisassignable[T](value:object,typx:TypeExpr[T])->TypeIs[T]:...

The use ofTypeExpr[]andTypeIs[]together enables type checkers to narrow the return type appropriately depending on what type expression is passed in:

request_json:object=...
ifisassignable(request_json,MyTypedDict):
assert_type(request_json,MyTypedDict)# type is narrowed!

Thatisassignablefunction enables a kind of enhancedisinstance check which is useful forchecking whether a value decoded from JSON conforms to a particular structureof nestedTypedDicts, lists, unions,Literals, and other types. This kind of check was alluded to inPEP 589but could not be implemented at the time without a notation similar to TypeExpr[].

Why can’ttype[]be used?

One might think you could define the example functions above to take a type[C]- which is syntax that already exists - rather than a TypeExpr[T].However if you were to do that then certain type expressions likestr|None- which are not class objects and therefore nottypes at runtime - would be rejected:

# NOTE: Uses a type[C] parameter rather than a TypeExpr[T]
deftrycast_type[C](typ:type[C],value:object)->T|None:...

trycast_type(str,'hi')# ok; str is a type
trycast_type(Optional[str],'hi')# ERROR; Optional[str] is not a type
trycast_type(str|int,'hi')# ERROR; (str | int) is not a type
trycast_type(MyTypedDict,dict(value='hi'))# questionable; accepted by mypy 1.9.0

To solve that problem,type[]could be widened to include the additional values allowed byTypeExpr.However doing so would lose type[]’s current ability to spell a class object which always supports instantiation andisinstancechecks, unlike arbitrary type expression objects. ThereforeTypeExpris proposed as new notation instead.

For a longer explanation of why we don’t just widentype[T]to accept all type expressions, see Widen type[C] to support all type expressions.

Common kinds of functions that would benefit from TypeExpr

A survey of various Python librariesrevealed a few kinds of commonly defined functions which would benefit fromTypeExpr[]:

  • Assignability checkers:
    • Returns whether a value is assignable to a type expression. If so then also narrows the type of the value to match the type expression.
    • Pattern 1: defisassignable[T](value:object,typx:TypeExpr[T])->TypeIs[T]
    • Pattern 2: defismatch[T](value:object,typx:TypeExpr[T])->TypeGuard[T]
    • Examples: beartype.is_bearable,trycast.isassignable, typeguard.check_type,xdsl.isa
  • Converters:
    • If a value is assignable to (or coercible to) a type expression, aconverterreturns the value narrowed to (or coerced to) that type expression. Otherwise, it raises an exception.
    • Pattern 1: defconvert[T](value:object,typx:TypeExpr[T])->T
    • Pattern 2:
      classConverter[T]:
      def__init__(self,typx:TypeExpr[T])->None:...
      defconvert(self,value:object)->T:...
      

The survey also identified some introspection functions that take annotation expressions as input using plainobjects which would notgain functionality by marking those inputs asTypeExpr[]:

  • General introspection operations:

Rationale

Before this PEP existed there were already a few definitions in use to describe different kinds of type annotations:

+----------------------------------+
| +------------------------------+ |
| | +-------------------------+ | |
| | | +---------------------+ | | |
| | | | Class object | | | | = type[C]
| | | +---------------------+ | | |
| | | Type expression object | | | = TypeExpr[T] <-- new!
| | +-------------------------+ | |
| | Annotation expression object | |
| +------------------------------+ |
| Object | = object
+----------------------------------+
  • Class objects, spelled astype[C],supportisinstancechecks and are callable.
    • Examples:int,str,MyClass
  • Type expressions include any type annotation which describes a type.
    • Examples:list[int],MyTypedDict,int|str, Literal['square'],any class object
  • Annotation expressions include any type annotation, including those only valid in specific contexts.
    • Examples:Final[int],Required[str],ClassVar[str], any type expression

TypeExpraligns with an existing definition from the above list - type expression- to avoid introducing yet another subset of type annotations that users of Python typing need to think about.

TypeExpraligns withtype expressionspecifically because a type expression is already used to parameterize type variables, which are used in combination withTypeIsandTypeGuardto enable the compelling examples mentioned inMotivation.

TypeExprdoes not align withannotation expressionfor reasons given in Rejected Ideas » Accept arbitrary annotation expressions.

Specification

ATypeExprvalue represents atype expression such asstr|None,dict[str,int],orMyTypedDict. ATypeExprtype is written as TypeExpr[T]whereTis a type or a type variable. It can also be written without brackets as justTypeExpr,which is treated the same as toTypeExpr[Any].

Using TypeExprs

ATypeExpris a new kind of type expression, usable in any context where a type expression is valid, as a function parameter type, a return type, or a variable type:

defis_union_type(typx:TypeExpr)->bool:...# parameter type
defunion_of[S,T](s:TypeExpr[S],t:TypeExpr[T])\
->TypeExpr[S|T]:...# return type
STR_TYPE:TypeExpr=str# variable type

Note however that anunannotatedvariable assigned a type expression literal will not be inferred to be ofTypeExprtype by type checkers because PEP 484reserves that syntax for defining type aliases:

  • No:
    STR_TYPE=str# OOPS; treated as a type alias!
    

If you want a type checker to recognize a type expression literal in a bare assignment you’ll need to explicitly declare the assignment-target as havingTypeExprtype:

  • Yes:
    STR_TYPE:TypeExpr=str
    
  • Yes:
    STR_TYPE:TypeExpr
    STR_TYPE=str
    
  • Okay, but discouraged:
    STR_TYPE=str# type: TypeExpr # the type comment is significant
    

TypeExprvalues can be passed around and assigned just like normal values:

defswap1[S,T](t1:TypeExpr[S],t2:TypeExpr[T])->tuple[TypeExpr[T],TypeExpr[S]]:
t1_new:TypeExpr[T]=t2# assigns a TypeExpr value to a new annotated variable
t2_new:TypeExpr[S]=t1
return(t1_new,t2_new)

defswap2[S,T](t1:TypeExpr[S],t2:TypeExpr[T])->tuple[TypeExpr[T],TypeExpr[S]]:
t1_new=t2# assigns a TypeExpr value to a new unannotated variable
t2_new=t1
assert_type(t1_new,TypeExpr[T])
assert_type(t2_new,TypeExpr[S])
return(t1_new,t2_new)

# NOTE: A more straightforward implementation would use isinstance()
defensure_int(value:object)->None:
value_type:TypeExpr=type(value)# assigns a type (a subtype of TypeExpr)
assertvalue_type==int

TypeExpr Values

A variable of typeTypeExpr[T]whereTis a type, can hold any type expression object- the result of evaluating a type expression at runtime - which is a subtype ofT.

Incomplete expressions like a bareOptionalorUnionwhich do not spell a type are notTypeExprvalues.

TypeExpr[...]is itself aTypeExprvalue:

OPTIONAL_INT_TYPE:TypeExpr=TypeExpr[int|None]# OK
assertisassignable(int|None,OPTIONAL_INT_TYPE)

TypeExpr[]values includealltype expressions including some non-universal type expressionswhich are not valid in all annotation contexts. In particular:

  • Self(valid only in some contexts)
  • TypeGuard[...](valid only in some contexts)
  • TypeIs[...](valid only in some contexts)

Explicit TypeExpr Values

The syntaxTypeExpr(T)(with parentheses) can be used to spell aTypeExpr[T]value explicitly:

NONE=TypeExpr(None)
INT1=TypeExpr('int')# stringified type expression
INT2=TypeExpr(int)

At runtime theTypeExpr(...)callable returns its single argument unchanged.

Implicit TypeExpr Values

Historically static type checkers have only needed to recognize type expressionsin contexts where a type expression was expected. Nowtype expression objectsmust also be recognized in contexts where a value expression is expected.

Static type checkers already recognizeclass objects(type[C]):

  • As a value expression,Chas typetype[C], for each of the following values of C:
    • name(wherenamemust refer to a valid in-scope class, type alias, or TypeVar)
    • name'['...']'
    • <type>'['...']'

The followingunparameterized type expressionscan be recognized unambiguously:

  • As a value expression,Xhas typeTypeExpr[X], for each of the following values of X:
    • <Any>
    • <Self>
    • <LiteralString>
    • <NoReturn>
    • <Never>

None:The type expressionNone(NoneType) is ambiguous with the valueNone, so must use the explicitTypeExpr(...)syntax:

  • As a value expression,TypeExpr(None)has typeTypeExpr[None].
  • As a value expression,Nonecontinues to have typeNone.

The followingparameterized type expressionscan be recognized unambiguously:

  • As a value expression,Xhas typeTypeExpr[X], for each of the following values of X:
    • <Literal>'['...']'
    • <Optional>'['...']'
    • <Union>'['...']'
    • <Callable>'['...']'
    • <tuple>'['...']'
    • <TypeGuard>'['...']'
    • <TypeIs>'['...']'

Annotated:The type expressionAnnotated[...]is ambiguous with the annotation expressionAnnotated[...], so must be disambiguated based on its argument type:

  • As a value expression,Annotated[x,...]has typetype[C] ifxhas typetype[C].
  • As a value expression,Annotated[x,...]has typeTypeExpr[T] ifxhas typeTypeExpr[T].
  • As a value expression,Annotated[x,...]has typeobject ifxhas a type that is nottype[C]orTypeExpr[T].

Union:The type expressionT1|T2is ambiguous with the valueint1|int2,set1|set2,dict1|dict2,and more, so must use the explicitTypeExpr(...)syntax:

  • Yes:
    ifisassignable(value,TypeExpr(int|str)):...
    
  • No:
    ifisassignable(value,int|str):...
    

Future PEPs may make it possible to recognize the value expressionT1|T2directly as an implicit TypeExpr value and avoid the need to use the explicitTypeExpr(...)syntax, but that work isdeferred for now.

Thestringified type expression"T"is ambiguous with both the stringified annotation expression"T" and the string literal"T", so must use the explicitTypeExpr(...)syntax:

  • As a value expression,TypeExpr( "T" )has typeTypeExpr[T], whereTis a valid type expression
  • As a value expression,"T"continues to have typeLiteral[ "T" ].

No other kinds of type expressions currently exist.

New kinds of type expressions that are introduced should define how they will be recognized in a value expression context.

Literal[] TypeExprs

A value ofLiteral[...]type isnotconsidered assignable to aTypeExprvariable even if all of its members spell valid types because dynamic values are not allowed in type expressions:

STRS_TYPE_NAME:Literal['str','list[str]']='str'
STRS_TYPE:TypeExpr=STRS_TYPE_NAME# ERROR: Literal[] value is not a TypeExpr

HoweverLiteral[...]itself is still aTypeExpr:

DIRECTION_TYPE:TypeExpr[Literal['left','right']]=Literal['left','right']# OK

Static vs. Runtime Representations of TypeExprs

ATypeExprvalue appearing statically in a source file may be normalized to a different representation at runtime. For example string-based forward references are normalized at runtime to beForwardRefinstances in some contexts:[4]

>>>IntTree=list[typing.Union[int,'IntTree']]
>>>IntTree
list[typing.Union[int, ForwardRef('IntTree')]]

The runtime representations ofTypeExprs are considered implementation details that may change over time and therefore static type checkers are not required to recognize them:

INT_TREE:TypeExpr=ForwardRef('IntTree')# ERROR: Runtime-only form

Runtime type checkers that wish to assign a runtime-only representation of a type expression to aTypeExpr[]variable must usecast()to avoid errors from static type checkers:

INT_TREE=cast(TypeExpr,ForwardRef('IntTree'))# OK

Subtyping

Whether aTypeExprvalue can be assigned from one variable to another is determined by the following rules:

Relationship with type

TypeExpr[]is covariant in its argument type, just liketype[]:

  • TypeExpr[T1]is a subtype ofTypeExpr[T2]iffT1is a subtype ofT2.
  • type[C1]is a subtype ofTypeExpr[C2]iffC1is a subtype ofC2.

An unparameterizedtypecan be assigned to an unparameterizedTypeExpr but not the other way around:

  • type[Any]is assignable toTypeExpr[Any].(But not the other way around.)

Relationship with object

TypeExpr[]is a kind ofobject,just liketype[]:

  • TypeExpr[T]for anyTis a subtype ofobject.

TypeExpr[T],whereTis a type variable, is assumed to have all the attributes and methods ofobjectand is not callable.

Interactions with isinstance() and issubclass()

TheTypeExprspecial form cannot be used as thetypeargument to isinstance:

>>>isinstance(str,TypeExpr)
TypeError: typing.TypeExpr cannot be used with isinstance()

>>>isinstance(str,TypeExpr[str])
TypeError: isinstance() argument 2 cannot be a parameterized generic

TheTypeExprspecial form cannot be used as any argument to issubclass:

>>>issubclass(TypeExpr,object)
TypeError: issubclass() arg 1 must be a class

>>>issubclass(object,TypeExpr)
TypeError: typing.TypeExpr cannot be used with issubclass()

Affected signatures in the standard library

Changed signatures

The following signatures related to type expressions introduce TypeExprwhere previouslyobjectorAnyexisted:

  • typing.cast
  • typing.assert_type

Unchanged signatures

The following signatures related to annotation expressions continue to useobjectand remain unchanged:

  • typing.get_origin
  • typing.get_args

The following signatures related to class objects continue to use typeand remain unchanged:

  • builtins.isinstance
  • builtins.issubclass
  • builtins.type

typing.get_type_hints(...,include_extras=False)nearly returns only type expressions in Python 3.12, stripping out most type qualifiers (Required,NotRequired,ReadOnly,Annotated) but currently preserves a few type qualifiers which are only allowed in annotation expressions (ClassVar,Final,InitVar,Unpack). It may be desirable to alter the behavior of this function in the future to also strip out those qualifiers and actually return type expressions, although this PEP does not propose those changes now:

  • typing.get_type_hints(...,include_extras=False)
    • Almost returns only type expressions, but not quite
  • typing.get_type_hints(...,include_extras=True)
    • Returns annotation expressions

Backwards Compatibility

The rules for recognizing type expression objects in a value expression context were not previously defined, so static type checkers varied in what types were assigned to such objects. Existing programs manipulating type expression objects were already limited in manipulating them as plainobjectvalues, and such programs should not break with the newly-defined rules.

How to Teach This

Normally when using type annotations in Python you’re concerned with defining the shape of values allowed to be passed to a function parameter, returned by a function, or stored in a variable:

parameter type return type
| |
v v
def plus(n1: int, n2: int) -> int:
sum: int = n1 + n2
^
|
variable type

return sum

However type annotations themselves are valid values in Python and can be assigned to variables and manipulated like any other data in a program:

a variable a type
| |
v v
MAYBE_INT_TYPE: TypeExpr = int | None
^
|
the type of a type

TypeExpr[]is how you spell the type of a variable containing a type annotation object describing a type.

TypeExpr[]is similar totype[],buttype[]can only spell simpleclass objectslikeint,str,list,orMyClass. TypeExpr[]by contrast can additionally spell more complex types, including those with brackets (likelist[int]) or pipes (likeint|None), and including special types likeAny,LiteralString,orNever.

ATypeExprvariable (maybe_float:TypeExpr) looks similar to aTypeAliasdefinition (MaybeFloat:TypeAlias), butTypeExpr can only be used where a dynamic value is expected:

  • No:
    maybe_float:TypeExpr=float|None
    defsqrt(n:float)->maybe_float:...# ERROR: Can't use TypeExpr value in a type annotation
    
  • Okay, but discouraged in Python 3.12+:
    MaybeFloat:TypeAlias=float|None
    defsqrt(n:float)->MaybeFloat:...
    
  • Yes:
    typeMaybeFloat=float|None
    defsqrt(n:float)->MaybeFloat:...
    

It is uncommon for a programmer to define theirownfunction which accepts aTypeExprparameter or returns aTypeExprvalue. Instead it is more common for a programmer to pass a literal type expression to anexistingfunction accepting aTypeExprinput which was imported from a runtime type checker library.

For example theisassignablefunction from thetrycastlibrary can be used like Python’s built-inisinstancefunction to check whether a value matches the shape of a particular type. isassignablewill acceptanykind of type as an input because its input is aTypeExpr.By contrastisinstanceonly accepts a simple class object (atype[]) as input:

  • Yes:
    fromtrycastimportisassignable
    
    ifisassignable(some_object,MyTypedDict):# OK: MyTypedDict is a TypeExpr[]
    ...
    
  • No:
    ifisinstance(some_object,MyTypedDict):# ERROR: MyTypedDict is not a type[]
    ...
    

There aremany other runtime type checkers providing useful functions that accept aTypeExpr.

Advanced Examples

If you want to write your own runtime type checker or some other kind of function that manipulates types as values at runtime, this section gives examples of how you might implement such a function usingTypeExpr.

Introspecting TypeExpr Values

ATypeExpris very similar to anobjectat runtime, with no additional attributes or methods defined.

You can use existing introspection functions liketyping.get_originand typing.get_argsto extract the components of a type expression that looks likeOrigin[Arg1,Arg2,...,ArgN]:

importtyping

defstrip_annotated_metadata(typx:TypeExpr[T])->TypeExpr[T]:
iftyping.get_origin(typx)istyping.Annotated:
typx=cast(TypeExpr[T],typing.get_args(typx)[0])
returntypx

You can also useisinstanceandisto distinguish one kind of type expression from another:

importtypes
importtyping

defsplit_union(typx:TypeExpr)->tuple[TypeExpr,...]:
ifisinstance(typx,types.UnionType):# X | Y
returncast(tuple[TypeExpr,...],typing.get_args(typx))
iftyping.get_origin(typx)istyping.Union:# Union[X, Y]
returncast(tuple[TypeExpr,...],typing.get_args(typx))
iftypxin(typing.Never,typing.NoReturn,):
return()
return(typx,)

Combining with a type variable

TypeExpr[]can be parameterized by a type variable that is used elsewhere within the same function definition:

defas_instance[T](typx:TypeExpr[T])->T|None:
returntypx()ifisinstance(typx,type)elseNone

Combining with type[]

BothTypeExpr[]andtype[]can be parameterized by the same type variable within the same function definition:

defas_type[T](typx:TypeExpr[T])->type[T]|None:
returntypxifisinstance(typx,type)elseNone

Combining with TypeIs[] and TypeGuard[]

A type variable parameterizing aTypeExpr[]can also be used by aTypeIs[] within the same function definition:

defisassignable[T](value:object,typx:TypeExpr[T])->TypeIs[T]:...

count:int|str=...
ifisassignable(count,int):
assert_type(count,int)
else:
assert_type(count,str)

or by aTypeGuard[]within the same function definition:

defisdefault[T](value:object,typx:TypeExpr[T])->TypeGuard[T]:
return(value==typx())ifisinstance(typx,type)elseFalse

value:int|str=''
ifisdefault(value,int):
assert_type(value,int)
assert0==value
elifisdefault(value,str):
assert_type(value,str)
assert''==value
else:
assert_type(value,int|str)

Challenges When Accepting All TypeExprs

A function that takes anarbitraryTypeExpras input must support a large variety of possible type expressions and is not easy to write. Some challenges faced by such a function include:

  • An ever-increasing number of typing special forms are introduced with each new Python version which must be recognized, with special handling required for each one.
  • Stringified type annotations[5](like'list[str]') must beparsed(to something liketyping.List[str]) to be introspected.
    • In practice it is extremely difficult for stringified type annotations to be handled reliably at runtime, so runtime type checkers may opt to not support them at all.
  • Resolving string-based forward references inside type expressions to actual values must typically be done usingeval(), which is difficult/impossible to use in a safe way.
  • Recursive types likeIntTree=list[typing.Union[int,'IntTree']] are not possible to fully resolve.
  • Supporting user-defined generic types (like Django’s QuerySet[User]) requires user-defined functions to recognize/parse, which a runtime type checker should provide a registration API for.

Reference Implementation

The following will be true when mypy#9773is implemented:

The mypy type checker supportsTypeExprtypes.

A reference implementation of the runtime component is provided in the typing_extensionsmodule.

Rejected Ideas

Widen type[C] to support all type expressions

typewasdesignedto only be used to describe class objects. A class object can always be used as the second argument ofisinstance() and can usually be instantiated by calling it.

TypeExpron the other hand is typically introspected by the user in some way, is not necessarily directly instantiable, and is not necessarily directly usable in a regularisinstance()check.

It would be possible to widentypeto include the additional values allowed byTypeExprbut it would reduce clarity about the user’s intentions when working with atype.Different concepts and usage patterns; different spellings.

Accept arbitrary annotation expressions

Certain typing special forms can be used insomebut notall annotation contexts:

For exampleFinal[]can be used as a variable type but not as a parameter type or a return type:

some_const:Final[str]=...# OK

deffoo(not_reassignable:Final[object]):...# ERROR: Final[] not allowed here

defnonsense()->Final[object]:...# ERROR: Final[] not meaningful here

TypeExpr[T]does not allow matching such annotation expressions because it is not clear what it would mean for such an expression to parameterized by a type variable in positionT:

defismatch[T](value:object,typx:TypeExpr[T])->TypeGuard[T]:...

deffoo(some_arg):
ifismatch(some_arg,Final[int]):# ERROR: Final[int] is not a TypeExpr
reveal_type(some_arg)#? NOT Final[int], because invalid for a parameter

Functions that wish to operate onallkinds of annotation expressions, including those that are notTypeExprs, can continue to accept such inputs asobjectparameters, as they must do so today.

Accept only universal type expressions

Earlier drafts of this PEP only allowedTypeExpr[]to match the subset of type expressions which are valid inallcontexts, excluding non-universal type expressions. However doing that would effectively create a new subset of annotation expressions that Python typing users would have to understand, on top of all the existing distinctions between “class objects”, “type expressions”, and “annotation expressions”.

To avoid introducing yet another concept that everyone has to learn, this proposal just roundsTypeExpr[]to exactly match the existing definition of a “type expression”.

Support pattern matching on type expressions

It was asserted that some functions may wish to pattern match on the interior of type expressions in their signatures.

One use case is to allow a function to explicitly enumerate all the specifickinds of type expressions it supports as input. Consider the following possible pattern matching syntax:

@overload
defcheckcast(typx:TypeExpr[AT=Annotated[T,*Anns]],value:str)->T:...
@overload
defcheckcast(typx:TypeExpr[UT=Union[*Ts]],value:str)->Union[*Ts]:...
@overload
defcheckcast(typx:type[C],value:str)->C:...
#... (more)

All functions observed in the wild that conceptually take aTypeExpr[] generally try to supportallkinds of type expressions, so it doesn’t seem valuable to enumerate a particular subset.

Additionally the above syntax isn’t precise enough to fully describe the actual input constraints for a typical function in the wild. For example many functions recognize un-stringified type expressions like list[Movie]but may not recognize type expressions with stringified subcomponents likelist['Movie'].

A second use case for pattern matching on the interior of type expressions is to explicitly match anAnnotated[]form to pull out the interior type argument and strip away the metadata:

defcheckcast(
typx:TypeExpr[T]|TypeExpr[AT=Annotated[T,*Anns]],
value:object
)->T:

HoweverAnnotated[T,metadata]is already treated equivalent toTanyway. There’s no additional value in being explicit about this behavior. The example above could be more-straightforwardly written as the equivalent:

defcheckcast(typx:TypeExpr[T],value:object)->T:

Recognize (T1 | T2) as an implicit TypeExpr value

It would be nice if a value expression likeint|strcould be recognized as an implicitTypeExprvalue and be used directly in a context where a TypeExprwas expected. However making that possible would require making changes to the rules that type checkers use for the|operator. These rules are currently underspecified and would need to be make explicit first, before making changes to them. The PEP author is not sufficiently motivated to take on that specification work at the time of writing.

Footnotes


Source:https://github / Python /peps/blob/main/peps/pep-0747.rst

Last modified:2024-08-20 10:29:32 GMT