Pyright: NewType does not type check when creating variables from a different type than base

Created on 14 Jul 2020  路  5Comments  路  Source: microsoft/pyright

Describe the bug
According to
https://www.python.org/dev/peps/pep-0484/#newtype-helper-function

For this NewType definition:

UserId = NewType('UserId', int)

This statement should fail type check:

UserId('user')          # Fails type check

but it does not.

To Reproduce
Just run the example from https://www.python.org/dev/peps/pep-0484/#newtype-helper-function

Expected behavior
Should not accept values that are not of type Base or derived from Base.

Screenshots or Code

UserId = NewType('UserId', int)

def name_by_id(user_id: UserId) -> str:
    ...

UserId('user')          # Should Fail type check but it does not

name_by_id(42)          # Fails type check
name_by_id(UserId(42))  # OK

num = UserId(5) + 1     # type: int

VS Code extension or command-line
Both:
VSCode extention: 1.1.52
CLI: $ pyright --version
pyright 1.1.52

addressed in next version bug

Most helpful comment

Ok, thanks for explaining it.

Moving on with the example from https://www.python.org/dev/peps/pep-0484/#newtype-helper-function

For a static type checker Derived = NewType('Derived', Base) is roughly equivalent to a definition:

class Derived(Base):
    def __init__(self, _x: Base) -> None:
        ...

If the NewType definition for UserId of base int:

UserId = NewType('UserId', int)

would be "roughly equivalent to":

class UserId(int):
    def __init__(self, _x: int) -> None:
        ...

then the typecheck would fail, as this code fails:

    class UserId(int):
        def __init__(self, _x: int) -> None:
            ...
    UserId('user')

All 5 comments

This statement should fail type check

Actually, this should not generate an error because the constructor for the int class accepts a str or bytes as an input parameter. For example, int("3") is permitted.

Ok, thanks for explaining it.

Moving on with the example from https://www.python.org/dev/peps/pep-0484/#newtype-helper-function

For a static type checker Derived = NewType('Derived', Base) is roughly equivalent to a definition:

class Derived(Base):
    def __init__(self, _x: Base) -> None:
        ...

If the NewType definition for UserId of base int:

UserId = NewType('UserId', int)

would be "roughly equivalent to":

class UserId(int):
    def __init__(self, _x: int) -> None:
        ...

then the typecheck would fail, as this code fails:

    class UserId(int):
        def __init__(self, _x: int) -> None:
            ...
    UserId('user')

Ah, you're right. I didn't read that section of the PEP closely enough.

This will be fixed in the next version of Pylance and Pyright.

This is now fixed in Pyright 1.1.53, which I just published. It will also be in the next version of Pylance.

Was this page helpful?
0 / 5 - 0 ratings