Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,7 @@ to the fact that `bool` is a `@final` class at runtime that cannot be subclassed

```py
from knot_extensions import Intersection, Not, AlwaysTruthy, AlwaysFalsy
from typing_extensions import Literal

class P: ...

Expand All @@ -686,6 +687,19 @@ def f(
reveal_type(f) # revealed: Never
reveal_type(g) # revealed: Never
reveal_type(h) # revealed: Never

def never(
a: Intersection[Intersection[AlwaysFalsy, Not[Literal[False]]], bool],
b: Intersection[Intersection[AlwaysTruthy, Not[Literal[True]]], bool],
c: Intersection[Intersection[Literal[True], Not[AlwaysTruthy]], bool],
d: Intersection[Intersection[Literal[False], Not[AlwaysFalsy]], bool],
):
# TODO: This should be `Never`
reveal_type(a) # revealed: Literal[True]
# TODO: This should be `Never`
reveal_type(b) # revealed: Literal[False]
Comment on lines +697 to +700
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two seem higher priority to follow up on, because we don't just fail to simplify the intersection, we simplify it to a plainly incorrect type.

reveal_type(c) # revealed: Never
reveal_type(d) # revealed: Never
```

## Simplification of `LiteralString`, `AlwaysTruthy` and `AlwaysFalsy`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ static_assert(not is_assignable_to(tuple[Any, Literal[2]], tuple[int, str]))
## Union types

```py
from knot_extensions import static_assert, is_assignable_to, Unknown
from typing import Literal, Any
from knot_extensions import AlwaysTruthy, AlwaysFalsy, static_assert, is_assignable_to, Unknown
from typing_extensions import Literal, Any, LiteralString

static_assert(is_assignable_to(int, int | str))
static_assert(is_assignable_to(str, int | str))
Expand All @@ -227,13 +227,22 @@ static_assert(not is_assignable_to(int | None, str | None))
static_assert(not is_assignable_to(Literal[1] | None, int))
static_assert(not is_assignable_to(Literal[1] | None, str | None))
static_assert(not is_assignable_to(Any | int | str, int))

# TODO: No errors
# error: [static-assert-error]
static_assert(is_assignable_to(bool, Literal[False] | AlwaysTruthy))
# error: [static-assert-error]
static_assert(is_assignable_to(bool, Literal[True] | AlwaysFalsy))
# error: [static-assert-error]
static_assert(is_assignable_to(LiteralString, Literal[""] | AlwaysTruthy))
static_assert(not is_assignable_to(Literal[True] | AlwaysFalsy, Literal[False] | AlwaysTruthy))
```

## Intersection types

```py
from knot_extensions import static_assert, is_assignable_to, Intersection, Not
from typing_extensions import Any, Literal
from knot_extensions import static_assert, is_assignable_to, Intersection, Not, AlwaysTruthy, AlwaysFalsy
from typing_extensions import Any, Literal, final, LiteralString

class Parent: ...
class Child1(Parent): ...
Expand Down Expand Up @@ -296,6 +305,19 @@ static_assert(is_assignable_to(Intersection[Any, Unrelated], Intersection[Any, P
static_assert(is_assignable_to(Intersection[Any, Parent, Unrelated], Intersection[Any, Parent, Unrelated]))
static_assert(is_assignable_to(Intersection[Unrelated, Any], Intersection[Unrelated, Not[Any]]))
static_assert(is_assignable_to(Intersection[Literal[1], Any], Intersection[Unrelated, Not[Any]]))

# TODO: No errors
# The condition `is_assignable_to(T & U, U)` should still be satisfied after the following transformations:
# `LiteralString & AlwaysTruthy` -> `LiteralString & ~Literal[""]`
# error: [static-assert-error]
static_assert(is_assignable_to(Intersection[LiteralString, Not[Literal[""]]], AlwaysTruthy))
# error: [static-assert-error]
static_assert(is_assignable_to(Intersection[LiteralString, Not[Literal["", "a"]]], AlwaysTruthy))
# `LiteralString & ~AlwaysFalsy` -> `LiteralString & ~Literal[""]`
# error: [static-assert-error]
static_assert(is_assignable_to(Intersection[LiteralString, Not[Literal[""]]], Not[AlwaysFalsy]))
# error: [static-assert-error]
static_assert(is_assignable_to(Intersection[LiteralString, Not[Literal["", "a"]]], Not[AlwaysFalsy]))
```

## General properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ static_assert(is_disjoint_from(None, Intersection[int, Not[str]]))

```py
from typing_extensions import Literal, LiteralString
from knot_extensions import TypeOf, is_disjoint_from, static_assert
from knot_extensions import Intersection, Not, TypeOf, is_disjoint_from, static_assert, AlwaysFalsy, AlwaysTruthy

static_assert(is_disjoint_from(Literal[True], Literal[False]))
static_assert(is_disjoint_from(Literal[True], Literal[1]))
Expand All @@ -223,6 +223,25 @@ static_assert(not is_disjoint_from(Literal[1], Literal[1]))
static_assert(not is_disjoint_from(Literal["a"], Literal["a"]))
static_assert(not is_disjoint_from(Literal["a"], LiteralString))
static_assert(not is_disjoint_from(Literal["a"], str))

# TODO: No errors
# error: [static-assert-error]
static_assert(is_disjoint_from(AlwaysFalsy, Intersection[LiteralString, Not[Literal[""]]]))
# error: [static-assert-error]
static_assert(is_disjoint_from(Intersection[Not[Literal[True]], Not[Literal[False]]], bool))
# error: [static-assert-error]
static_assert(is_disjoint_from(Intersection[AlwaysFalsy, Not[Literal[False]]], bool))
# error: [static-assert-error]
static_assert(is_disjoint_from(Intersection[AlwaysTruthy, Not[Literal[True]]], bool))

# TODO: No errors
# The condition `is_disjoint(T, Not[T])` must still be satisfied after the following transformations:
# `LiteralString & AlwaysTruthy` -> `LiteralString & ~Literal[""]`
# error: [static-assert-error]
static_assert(is_disjoint_from(Intersection[LiteralString, AlwaysTruthy], Not[LiteralString] | AlwaysFalsy))
# `LiteralString & ~AlwaysFalsy` -> `LiteralString & ~Literal[""]`
# error: [static-assert-error]
static_assert(is_disjoint_from(Intersection[LiteralString, Not[AlwaysFalsy]], Not[LiteralString] | AlwaysFalsy))
```

### Class, module and function literals
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ static_assert(is_gradual_equivalent_to(Intersection[str | int, Not[type[Any]]],

static_assert(not is_gradual_equivalent_to(str | int, int | str | bytes))
static_assert(not is_gradual_equivalent_to(str | int | bytes, int | str | dict))

# TODO: No errors
# error: [static-assert-error]
static_assert(is_gradual_equivalent_to(Unknown, Unknown | Any))
# error: [static-assert-error]
static_assert(is_gradual_equivalent_to(Unknown, Intersection[Unknown, Any]))
Comment on lines +50 to +54
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the dynamic type reduction from your previous PR, which would fix these cases, is pretty solid (not overly special-cased) and could be put up as a separate PR?

```

## Tuples
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,8 @@ static_assert(is_subtype_of(Never, AlwaysFalsy))
### `AlwaysTruthy` and `AlwaysFalsy`

```py
from knot_extensions import AlwaysTruthy, AlwaysFalsy, is_subtype_of, static_assert
from typing import Literal
from knot_extensions import AlwaysTruthy, AlwaysFalsy, Intersection, Not, is_subtype_of, static_assert
from typing_extensions import Literal, LiteralString

static_assert(is_subtype_of(Literal[1], AlwaysTruthy))
static_assert(is_subtype_of(Literal[0], AlwaysFalsy))
Expand All @@ -290,6 +290,28 @@ static_assert(not is_subtype_of(Literal[0], AlwaysTruthy))

static_assert(not is_subtype_of(str, AlwaysTruthy))
static_assert(not is_subtype_of(str, AlwaysFalsy))

# TODO: No errors
# error: [static-assert-error]
static_assert(is_subtype_of(bool, Literal[False] | AlwaysTruthy))
# error: [static-assert-error]
static_assert(is_subtype_of(bool, Literal[True] | AlwaysFalsy))
# error: [static-assert-error]
static_assert(is_subtype_of(LiteralString, Literal[""] | AlwaysTruthy))
static_assert(not is_subtype_of(Literal[True] | AlwaysFalsy, Literal[False] | AlwaysTruthy))

# TODO: No errors
# The condition `is_subtype_of(T & U, U)` must still be satisfied after the following transformations:
# `LiteralString & AlwaysTruthy` -> `LiteralString & ~Literal[""]`
# error: [static-assert-error]
static_assert(is_subtype_of(Intersection[LiteralString, Not[Literal[""]]], AlwaysTruthy))
# error: [static-assert-error]
static_assert(is_subtype_of(Intersection[LiteralString, Not[Literal["", "a"]]], AlwaysTruthy))
# `LiteralString & ~AlwaysFalsy` -> `LiteralString & ~Literal[""]`
# error: [static-assert-error]
static_assert(is_subtype_of(Intersection[LiteralString, Not[Literal[""]]], Not[AlwaysFalsy]))
# error: [static-assert-error]
static_assert(is_subtype_of(Intersection[LiteralString, Not[Literal["", "a"]]], Not[AlwaysFalsy]))
```

### Module literals
Expand Down