@@ -47,7 +47,7 @@ The invariant position is replaced with an unresolved type variable.
47
47
48
48
``` py
49
49
def _ (top_list : Top[list[Any]]):
50
- reveal_type(top_list) # revealed: list[T_all ]
50
+ reveal_type(top_list) # revealed: Top[ list[Any] ]
51
51
```
52
52
53
53
### Bottom materialization
@@ -75,7 +75,7 @@ type variable.
75
75
76
76
``` py
77
77
def _ (bottom_list : Bottom[list[Any]]):
78
- reveal_type(bottom_list) # revealed: list[T_all ]
78
+ reveal_type(bottom_list) # revealed: Bottom[ list[Any] ]
79
79
```
80
80
81
81
## Fully static types
@@ -230,14 +230,14 @@ def _(
230
230
top_aiu : Top[LTAnyIntUnknown],
231
231
bottom_aiu : Bottom[LTAnyIntUnknown],
232
232
):
233
- reveal_type(top_ai) # revealed: list[tuple[T_all , int]]
234
- reveal_type(bottom_ai) # revealed: list[tuple[T_all , int]]
233
+ reveal_type(top_ai) # revealed: Top[ list[tuple[Any , int] ]]
234
+ reveal_type(bottom_ai) # revealed: Bottom[ list[tuple[Any , int] ]]
235
235
236
- reveal_type(top_su) # revealed: list[tuple[str, T_all ]]
237
- reveal_type(bottom_su) # revealed: list[tuple[str, T_all ]]
236
+ reveal_type(top_su) # revealed: Top[ list[tuple[str, Unknown] ]]
237
+ reveal_type(bottom_su) # revealed: Bottom[ list[tuple[str, Unknown] ]]
238
238
239
- reveal_type(top_aiu) # revealed: list[tuple[T_all , int, T_all ]]
240
- reveal_type(bottom_aiu) # revealed: list[tuple[T_all , int, T_all ]]
239
+ reveal_type(top_aiu) # revealed: Top[ list[tuple[Any , int, Unknown] ]]
240
+ reveal_type(bottom_aiu) # revealed: Bottom[ list[tuple[Any , int, Unknown] ]]
241
241
```
242
242
243
243
## Union
@@ -286,14 +286,14 @@ def _(
286
286
top_aiu : Top[list[Any | int | Unknown]],
287
287
bottom_aiu : Bottom[list[Any | int | Unknown]],
288
288
):
289
- reveal_type(top_ai) # revealed: list[T_all | int]
290
- reveal_type(bottom_ai) # revealed: list[T_all | int]
289
+ reveal_type(top_ai) # revealed: Top[ list[Any | int] ]
290
+ reveal_type(bottom_ai) # revealed: Bottom[ list[Any | int] ]
291
291
292
- reveal_type(top_su) # revealed: list[str | T_all ]
293
- reveal_type(bottom_su) # revealed: list[str | T_all ]
292
+ reveal_type(top_su) # revealed: Top[ list[str | Unknown] ]
293
+ reveal_type(bottom_su) # revealed: Bottom[ list[str | Unknown] ]
294
294
295
- reveal_type(top_aiu) # revealed: list[T_all | int]
296
- reveal_type(bottom_aiu) # revealed: list[T_all | int]
295
+ reveal_type(top_aiu) # revealed: Top[ list[Any | int] ]
296
+ reveal_type(bottom_aiu) # revealed: Bottom[ list[Any | int] ]
297
297
```
298
298
299
299
## Intersection
@@ -320,8 +320,10 @@ def _(
320
320
top : Top[Intersection[list[Any], list[int ]]],
321
321
bottom : Bottom[Intersection[list[Any], list[int ]]],
322
322
):
323
- reveal_type(top) # revealed: list[T_all] & list[int]
324
- reveal_type(bottom) # revealed: list[T_all] & list[int]
323
+ # Top[list[Any] & list[int]] = Top[list[Any]] & list[int] = list[int]
324
+ reveal_type(top) # revealed: list[int]
325
+ # Bottom[list[Any] & list[int]] = Bottom[list[Any]] & list[int] = Bottom[list[Any]]
326
+ reveal_type(bottom) # revealed: Bottom[list[Any]]
325
327
```
326
328
327
329
## Negation (via ` Not ` )
@@ -366,8 +368,8 @@ static_assert(is_equivalent_to(Bottom[type[int | Any]], type[int]))
366
368
367
369
# Here, `T` has an upper bound of `type`
368
370
def _ (top : Top[list[type[Any]]], bottom : Bottom[list[type[Any]]]):
369
- reveal_type(top) # revealed: list[T_all ]
370
- reveal_type(bottom) # revealed: list[T_all ]
371
+ reveal_type(top) # revealed: Top[ list[type[Any]] ]
372
+ reveal_type(bottom) # revealed: Bottom[ list[type[Any]] ]
371
373
```
372
374
373
375
## Type variables
@@ -427,8 +429,8 @@ class GenericContravariant(Generic[T_contra]):
427
429
pass
428
430
429
431
def _ (top : Top[GenericInvariant[Any]], bottom : Bottom[GenericInvariant[Any]]):
430
- reveal_type(top) # revealed: GenericInvariant[T_all ]
431
- reveal_type(bottom) # revealed: GenericInvariant[T_all ]
432
+ reveal_type(top) # revealed: Top[ GenericInvariant[Any] ]
433
+ reveal_type(bottom) # revealed: Bottom[ GenericInvariant[Any] ]
432
434
433
435
static_assert(is_equivalent_to(Top[GenericCovariant[Any]], GenericCovariant[object ]))
434
436
static_assert(is_equivalent_to(Bottom[GenericCovariant[Any]], GenericCovariant[Never]))
@@ -448,8 +450,8 @@ type CovariantCallable = Callable[[GenericCovariant[Any]], None]
448
450
type ContravariantCallable = Callable[[GenericContravariant[Any]], None ]
449
451
450
452
def invariant (top : Top[InvariantCallable], bottom : Bottom[InvariantCallable]) -> None :
451
- reveal_type(top) # revealed: (GenericInvariant[T_all ], /) -> None
452
- reveal_type(bottom) # revealed: (GenericInvariant[T_all ], /) -> None
453
+ reveal_type(top) # revealed: (Bottom[ GenericInvariant[Any] ], /) -> None
454
+ reveal_type(bottom) # revealed: (Top[ GenericInvariant[Any] ], /) -> None
453
455
454
456
def covariant (top : Top[CovariantCallable], bottom : Bottom[CovariantCallable]) -> None :
455
457
reveal_type(top) # revealed: (GenericCovariant[Never], /) -> None
@@ -492,3 +494,207 @@ def _(
492
494
bottom_1 : Bottom[1 ], # error: [invalid-type-form]
493
495
): ...
494
496
```
497
+
498
+ ## Nested use
499
+
500
+ ` Top[T] ` and ` Bottom[T] ` are always fully static types. Therefore, they have only one
501
+ materialization (themselves) and applying ` Top ` or ` Bottom ` again does nothing.
502
+
503
+ ``` py
504
+ from typing import Any
505
+ from ty_extensions import Top, Bottom, static_assert, is_equivalent_to
506
+
507
+ static_assert(is_equivalent_to(Top[Top[list[Any]]], Top[list[Any]]))
508
+ static_assert(is_equivalent_to(Bottom[Top[list[Any]]], Top[list[Any]]))
509
+
510
+ static_assert(is_equivalent_to(Bottom[Bottom[list[Any]]], Bottom[list[Any]]))
511
+ static_assert(is_equivalent_to(Top[Bottom[list[Any]]], Bottom[list[Any]]))
512
+ ```
513
+
514
+ ## Subtyping
515
+
516
+ Any ` list[T] ` is a subtype of ` Top[list[Any]] ` , but with more restrictive gradual types, not all
517
+ other specializations are subtypes.
518
+
519
+ ``` py
520
+ from typing import Any, Literal
521
+ from ty_extensions import is_subtype_of, static_assert, Top, Intersection, Bottom
522
+
523
+ # None and Top
524
+ static_assert(is_subtype_of(list[int ], Top[list[Any]]))
525
+ static_assert(not is_subtype_of(Top[list[Any]], list[int ]))
526
+ static_assert(is_subtype_of(list[bool ], Top[list[Intersection[int , Any]]]))
527
+ static_assert(is_subtype_of(list[int ], Top[list[Intersection[int , Any]]]))
528
+ static_assert(not is_subtype_of(list[int | str ], Top[list[Intersection[int , Any]]]))
529
+ static_assert(not is_subtype_of(list[object ], Top[list[Intersection[int , Any]]]))
530
+ static_assert(not is_subtype_of(list[str ], Top[list[Intersection[int , Any]]]))
531
+ static_assert(not is_subtype_of(list[str | bool ], Top[list[Intersection[int , Any]]]))
532
+
533
+ # Top and Top
534
+ static_assert(is_subtype_of(Top[list[int | Any]], Top[list[Any]]))
535
+ static_assert(not is_subtype_of(Top[list[Any]], Top[list[int | Any]]))
536
+ static_assert(is_subtype_of(Top[list[Intersection[int , Any]]], Top[list[Any]]))
537
+ static_assert(not is_subtype_of(Top[list[Any]], Top[list[Intersection[int , Any]]]))
538
+ static_assert(not is_subtype_of(Top[list[Intersection[int , Any]]], Top[list[int | Any]]))
539
+ static_assert(not is_subtype_of(Top[list[int | Any]], Top[list[Intersection[int , Any]]]))
540
+ static_assert(not is_subtype_of(Top[list[str | Any]], Top[list[int | Any]]))
541
+ static_assert(is_subtype_of(Top[list[str | int | Any]], Top[list[int | Any]]))
542
+ static_assert(not is_subtype_of(Top[list[int | Any]], Top[list[str | int | Any]]))
543
+
544
+ # Bottom and Top
545
+ static_assert(is_subtype_of(Bottom[list[Any]], Top[list[Any]]))
546
+ static_assert(is_subtype_of(Bottom[list[Any]], Top[list[int | Any]]))
547
+ static_assert(is_subtype_of(Bottom[list[int | Any]], Top[list[Any]]))
548
+ static_assert(is_subtype_of(Bottom[list[int | Any]], Top[list[int | str ]]))
549
+ static_assert(is_subtype_of(Bottom[list[Intersection[int , Any]]], Top[list[Intersection[str , Any]]]))
550
+ static_assert(not is_subtype_of(Bottom[list[Intersection[int , bool | Any]]], Bottom[list[Intersection[str , Literal[" x" ] | Any]]]))
551
+
552
+ # None and None
553
+ static_assert(not is_subtype_of(list[int ], list[Any]))
554
+ static_assert(not is_subtype_of(list[Any], list[int ]))
555
+ static_assert(is_subtype_of(list[int ], list[int ]))
556
+ static_assert(not is_subtype_of(list[int ], list[object ]))
557
+ static_assert(not is_subtype_of(list[object ], list[int ]))
558
+
559
+ # Top and None
560
+ static_assert(not is_subtype_of(Top[list[Any]], list[Any]))
561
+ static_assert(not is_subtype_of(Top[list[Any]], list[int ]))
562
+ static_assert(is_subtype_of(Top[list[int ]], list[int ]))
563
+
564
+ # Bottom and None
565
+ static_assert(is_subtype_of(Bottom[list[Any]], list[object ]))
566
+ static_assert(is_subtype_of(Bottom[list[int | Any]], list[str | int ]))
567
+ static_assert(not is_subtype_of(Bottom[list[str | Any]], list[Intersection[int , bool | Any]]))
568
+
569
+ # None and Bottom
570
+ static_assert(not is_subtype_of(list[int ], Bottom[list[Any]]))
571
+ static_assert(not is_subtype_of(list[int ], Bottom[list[int | Any]]))
572
+ static_assert(is_subtype_of(list[int ], Bottom[list[int ]]))
573
+
574
+ # Top and Bottom
575
+ static_assert(not is_subtype_of(Top[list[Any]], Bottom[list[Any]]))
576
+ static_assert(not is_subtype_of(Top[list[int | Any]], Bottom[list[int | Any]]))
577
+ static_assert(is_subtype_of(Top[list[int ]], Bottom[list[int ]]))
578
+
579
+ # Bottom and Bottom
580
+ static_assert(is_subtype_of(Bottom[list[Any]], Bottom[list[int | str | Any]]))
581
+ static_assert(is_subtype_of(Bottom[list[int | Any]], Bottom[list[int | str | Any]]))
582
+ static_assert(is_subtype_of(Bottom[list[bool | Any]], Bottom[list[int | Any]]))
583
+ static_assert(not is_subtype_of(Bottom[list[int | Any]], Bottom[list[bool | Any]]))
584
+ static_assert(not is_subtype_of(Bottom[list[int | Any]], Bottom[list[Any]]))
585
+ ```
586
+
587
+ ## Assignability
588
+
589
+ ### General
590
+
591
+ Assignability is the same as subtyping for top and bottom materializations, because those are fully
592
+ static types, but some gradual types are assignable even if they are not subtypes.
593
+
594
+ ``` py
595
+ from typing import Any, Literal
596
+ from ty_extensions import is_assignable_to, static_assert, Top, Intersection, Bottom
597
+
598
+ # None and Top
599
+ static_assert(is_assignable_to(list[Any], Top[list[Any]]))
600
+ static_assert(is_assignable_to(list[int ], Top[list[Any]]))
601
+ static_assert(not is_assignable_to(Top[list[Any]], list[int ]))
602
+ static_assert(is_assignable_to(list[bool ], Top[list[Intersection[int , Any]]]))
603
+ static_assert(is_assignable_to(list[int ], Top[list[Intersection[int , Any]]]))
604
+ static_assert(is_assignable_to(list[Any], Top[list[Intersection[int , Any]]]))
605
+ static_assert(not is_assignable_to(list[int | str ], Top[list[Intersection[int , Any]]]))
606
+ static_assert(not is_assignable_to(list[object ], Top[list[Intersection[int , Any]]]))
607
+ static_assert(not is_assignable_to(list[str ], Top[list[Intersection[int , Any]]]))
608
+ static_assert(not is_assignable_to(list[str | bool ], Top[list[Intersection[int , Any]]]))
609
+
610
+ # Top and Top
611
+ static_assert(is_assignable_to(Top[list[int | Any]], Top[list[Any]]))
612
+ static_assert(not is_assignable_to(Top[list[Any]], Top[list[int | Any]]))
613
+ static_assert(is_assignable_to(Top[list[Intersection[int , Any]]], Top[list[Any]]))
614
+ static_assert(not is_assignable_to(Top[list[Any]], Top[list[Intersection[int , Any]]]))
615
+ static_assert(not is_assignable_to(Top[list[Intersection[int , Any]]], Top[list[int | Any]]))
616
+ static_assert(not is_assignable_to(Top[list[int | Any]], Top[list[Intersection[int , Any]]]))
617
+ static_assert(not is_assignable_to(Top[list[str | Any]], Top[list[int | Any]]))
618
+ static_assert(is_assignable_to(Top[list[str | int | Any]], Top[list[int | Any]]))
619
+ static_assert(not is_assignable_to(Top[list[int | Any]], Top[list[str | int | Any]]))
620
+
621
+ # Bottom and Top
622
+ static_assert(is_assignable_to(Bottom[list[Any]], Top[list[Any]]))
623
+ static_assert(is_assignable_to(Bottom[list[Any]], Top[list[int | Any]]))
624
+ static_assert(is_assignable_to(Bottom[list[int | Any]], Top[list[Any]]))
625
+ static_assert(is_assignable_to(Bottom[list[Intersection[int , Any]]], Top[list[Intersection[str , Any]]]))
626
+ static_assert(
627
+ not is_assignable_to(Bottom[list[Intersection[int , bool | Any]]], Bottom[list[Intersection[str , Literal[" x" ] | Any]]])
628
+ )
629
+
630
+ # None and None
631
+ static_assert(is_assignable_to(list[int ], list[Any]))
632
+ static_assert(is_assignable_to(list[Any], list[int ]))
633
+ static_assert(is_assignable_to(list[int ], list[int ]))
634
+ static_assert(not is_assignable_to(list[int ], list[object ]))
635
+ static_assert(not is_assignable_to(list[object ], list[int ]))
636
+
637
+ # Top and None
638
+ static_assert(is_assignable_to(Top[list[Any]], list[Any]))
639
+ static_assert(not is_assignable_to(Top[list[Any]], list[int ]))
640
+ static_assert(is_assignable_to(Top[list[int ]], list[int ]))
641
+
642
+ # Bottom and None
643
+ static_assert(is_assignable_to(Bottom[list[Any]], list[object ]))
644
+ static_assert(is_assignable_to(Bottom[list[int | Any]], Top[list[str | int ]]))
645
+ static_assert(not is_assignable_to(Bottom[list[str | Any]], list[Intersection[int , bool | Any]]))
646
+
647
+ # None and Bottom
648
+ static_assert(is_assignable_to(list[Any], Bottom[list[Any]]))
649
+ static_assert(not is_assignable_to(list[int ], Bottom[list[Any]]))
650
+ static_assert(not is_assignable_to(list[int ], Bottom[list[int | Any]]))
651
+ static_assert(is_assignable_to(list[int ], Bottom[list[int ]]))
652
+
653
+ # Top and Bottom
654
+ static_assert(not is_assignable_to(Top[list[Any]], Bottom[list[Any]]))
655
+ static_assert(not is_assignable_to(Top[list[int | Any]], Bottom[list[int | Any]]))
656
+ static_assert(is_assignable_to(Top[list[int ]], Bottom[list[int ]]))
657
+
658
+ # Bottom and Bottom
659
+ static_assert(is_assignable_to(Bottom[list[Any]], Bottom[list[int | str | Any]]))
660
+ static_assert(is_assignable_to(Bottom[list[int | Any]], Bottom[list[int | str | Any]]))
661
+ static_assert(is_assignable_to(Bottom[list[bool | Any]], Bottom[list[int | Any]]))
662
+ static_assert(not is_assignable_to(Bottom[list[int | Any]], Bottom[list[bool | Any]]))
663
+ static_assert(not is_assignable_to(Bottom[list[int | Any]], Bottom[list[Any]]))
664
+ ```
665
+
666
+ ### Subclasses with different variance
667
+
668
+ We need to take special care when an invariant class inherits from a covariant or contravariant one.
669
+ This comes up frequently in practice because ` list ` (invariant) inherits from ` Sequence ` and a
670
+ number of other covariant ABCs, but we'll use a synthetic example.
671
+
672
+ ``` py
673
+ from typing import Generic, TypeVar, Any
674
+ from ty_extensions import static_assert, is_assignable_to, is_equivalent_to, Top
675
+
676
+ class A :
677
+ pass
678
+
679
+ class B (A ):
680
+ pass
681
+
682
+ T_co = TypeVar(" T_co" , covariant = True )
683
+ T = TypeVar(" T" )
684
+
685
+ class CovariantBase (Generic[T_co]):
686
+ def get (self ) -> T_co:
687
+ raise NotImplementedError
688
+
689
+ class InvariantChild (CovariantBase[T]):
690
+ def push (self , obj : T) -> None : ...
691
+
692
+ static_assert(is_assignable_to(InvariantChild[A], CovariantBase[A]))
693
+ static_assert(is_assignable_to(InvariantChild[B], CovariantBase[A]))
694
+ static_assert(not is_assignable_to(InvariantChild[A], CovariantBase[B]))
695
+ static_assert(not is_assignable_to(InvariantChild[B], InvariantChild[A]))
696
+ static_assert(is_equivalent_to(Top[CovariantBase[Any]], CovariantBase[object ]))
697
+ static_assert(is_assignable_to(InvariantChild[Any], CovariantBase[A]))
698
+
699
+ static_assert(not is_assignable_to(Top[InvariantChild[Any]], CovariantBase[A]))
700
+ ```
0 commit comments