Skip to content

Review User API Type Hints #794

Open

Description

@Archmonger

Current Situation

ReactPy can generate type hinting errors generated (from Pylance).

For example, the fact that reactpy.component can ingest a ComponentType, but can only return a Component. Or the fact that use_state can be set to None initially and changed later.

Proposed Actions

Review user API type hints and fix where needed.

Activity

  1. added this to the milestone on Jul 29, 2022
  2. rmorshea commented on Jul 30, 2022

    @rmorshea
    Collaborator

    I think idom.component is typed correctly. It's input function is a render function which may return ComponentType | VdomDict | None and its output is a constructor for a Component object. The distinction between Component and ComponentType is that the latter is a protocol, an abstract interface. Component on the other hand is a concrete implementation of ComponentType. ContextProvider is another example of a ComponentType.

    For use_state, if its value is initially None but may also be some other type (e.g. a str) then you must explicitly declare this upfront.

    initial_value: str | None = None
    value, set_value = use_state(initial_value)
    
    # Or cast initial value
    
    from typing import cast
    value, set_value = use_state(cast(str | None, None))
    
    # Or declare state, set_state upfront
    
    value: str | None
    set_value: Callable[[str | None], None]
    value, set_value = use_state(None)

    Admittedly, while these solutions work, I think they all feel a bit awkward. Here are two alternatives:

    # Use a named tuple
    state: State[str | None] = use_state(None)
    value, set_value = state
    state.value
    state.set_value
    
    # Or do some weird typing magic to make this possible
    value, set_value = use_state[str | None](None)

    I haven't seen the latter pattern used very many places so it might surprise people, but it looks nice.

  3. rmorshea commented on Jul 30, 2022

    @rmorshea
    Collaborator

    On thinking further, use_state is complicated by the fact that Python 3.9 broke generic NamedTuples which we'd need to get this working. The best we'd be able to do is a generic tuple till this is fixed in Python 3.11.

  4. rmorshea commented on Sep 27, 2022

    @rmorshea
    Collaborator

    Mypy 0981 includes support for generic NamedTuples.

  5. rmorshea commented on Sep 27, 2022

    @rmorshea
    Collaborator

    Also, presently the @component hides the type annotations of the underlying rendering function. This is because @component adds the special key parameter that is not consumed by the rendering function. Typing this correctly is blocked on concatenating keyword-only arguments with ParamSpec

  6. Archmonger commented on Oct 7, 2022

    @Archmonger
    ContributorAuthor

    Currently getting type hint errors on the component decorator, if str() is directly returned within a component. Or if a component is directly returned within a component.

  7. Archmonger commented on Oct 19, 2022

    @Archmonger
    ContributorAuthor

    VDOM constructor type hints currently don't like None.

  8. rmorshea commented on Oct 19, 2022

    @rmorshea
    Collaborator

    In the case a None child is passed?

  9. Archmonger commented on Oct 19, 2022

    @Archmonger
    ContributorAuthor

    Yeah, for example a conditional to return a component or None within a div tag.

  10. Archmonger commented on Oct 19, 2022

    @Archmonger
    ContributorAuthor

    It's also still very annoying that I can't directly return a component within a component without type hint errors.

  11. rmorshea commented on Oct 19, 2022

    @rmorshea
    Collaborator

    a conditional to return a component or None within a div tag.

    Can you share an example. I'm not quite sure I understand what you're describing.

    I can't directly return a component within a component

    Can you share the error you're getting? The type hint for the allowable return type for component functions should allow for this.

  12. Archmonger commented on Oct 19, 2022

    @Archmonger
    ContributorAuthor
    Argument of type "None" cannot be assigned to parameter "attributes_and_children" of type "VdomAttributesAndChildren" in function "__call__"
      Type "None" cannot be assigned to type "VdomAttributesAndChildren"
        Type "None" cannot be assigned to type "Mapping[str, Any]"
        "__iter__" is not present
        "key" is not present
        "type" is not present
        "render" is not present
        "should_render" is not present
        Type "None" cannot be assigned to type "VdomDict"
      ...Pylance[reportGeneralTypeIssues](https://github.com/microsoft/pylance-release/blob/main/DIAGNOSTIC_SEVERITY_RULES.md#diagnostic-severity-rules)
    
    @component
    def component_1():
       return html.div(
          None
       )

    Argument of type "(*args: Unknown, **kwargs: Unknown) -> (Component | Unknown)" cannot be assigned to parameter "function" of type "(...) -> (ComponentType | VdomDict | None)" in function "component"
      Type "(*args: Unknown, **kwargs: Unknown) -> (Component | Unknown)" cannot be assigned to type "(...) -> (ComponentType | VdomDict | None)"
        Function return type "Component | Unknown" is incompatible with type "ComponentType | VdomDict | None"
          Type "Component | Unknown" cannot be assigned to type "ComponentType | VdomDict | None"
            Type "Component" cannot be assigned to type "ComponentType | VdomDict | None"
              "Component" is incompatible with protocol "ComponentType"
              "Component" is incompatible with "VdomDict"
              Type cannot be assigned to type "None"Pylance[reportGeneralTypeIssues](https://github.com/microsoft/pylance-release/blob/main/DIAGNOSTIC_SEVERITY_RULES.md#diagnostic-severity-rules)
    
    @component
    def component_2():
       return html.div()
    
    @component
    def component_1():
       return component_2()
  13. 16 remaining items

  14. rmorshea commented on Jan 27, 2023

    @rmorshea
    Collaborator

    Can you elaborate on what those issues are?

  15. Archmonger commented on Jan 27, 2023

    @Archmonger
    ContributorAuthor

    Seems like the main MyPy issue is _Type is not correctly inferred for state and set_state.

    Basically every usage of state/set_state result errors similar to
    Argument 1 has incompatible type "bool"; expected "Union[_Type, Callable[[_Type], _Type]]"
    or
    Argument 2 to "Mutation" has incompatible type "_Type"; expected "bool"

    You can see this in django_idom.hooks.

  16. rmorshea commented on Jan 27, 2023

    @rmorshea
    Collaborator

    I've been noticing some weird behavior with my MyPy cache. Does the error persist if you clear the cache first?

  17. Archmonger commented on Jan 27, 2023

    @Archmonger
    ContributorAuthor

    Clearing cache fixed the issues.

    How strange... Maybe we can see if sqlite_cache resolves this issue?

  18. rmorshea commented on Jan 27, 2023

    @rmorshea
    Collaborator

    Yeah, I have no idea what's going on. I've been meaning to try and write up a bug report.

  19. Archmonger commented on Jan 30, 2023

    @Archmonger
    ContributorAuthor

    The issue kept returning in my environment.

    Changing sqlite_cache and cache_fine_grained did not resolve the issue.

    However, it seems like changing incremental to false does resolve the issue.

  20. rmorshea commented on Jan 30, 2023

    @rmorshea
    Collaborator

    That's good to know, I've been manually deleting the cache on every run.

  21. removed this from the milestone on Feb 21, 2023
  22. Archmonger commented on Sep 25, 2023

    @Archmonger
    ContributorAuthor

    Note to self:

    The type hints around reactpy.html elements is too strict. Frankly, we may as well just accept Any for children and have the end-user handle incompatibilities.

  23. Archmonger commented on Oct 12, 2024

    @Archmonger
    ContributorAuthor

    VdomAttributes should be rewritten to dict[str, Any] since type hinters really don't giving any auto completion for MutableMapping.

  24. Archmonger commented on Oct 13, 2024

    @Archmonger
    ContributorAuthor

    The Context type created by create_context() currently suggests that a list of elements can be provided. This is unfortunately false, and will result in rendering failures.

    Either remove this type hint, or begin official support for lists in Context.

  25. Archmonger commented on Oct 15, 2024

    @Archmonger
    ContributorAuthor

    None type is currently an accepted type for Context, but is rendered as a literal string None when used as a child element.

  26. Archmonger commented on Dec 4, 2024

    @Archmonger
    ContributorAuthor

    VdomDict and VdomChild are currently incompatible types, despite being the same thing. We should remove one of them.

  27. Archmonger commented on Dec 11, 2024

    @Archmonger
    ContributorAuthor

    Layout currently only accepts ComponentType as an arg, but Context should be allowed as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority-3-lowMay be resolved one any timeline.type-refactorAbout improving code structure.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions