Skip to content

disjoint unions in mypy? #6863

Closed
Closed
@kurtgn

Description

@kurtgn

I am trying to customize a function that takes an object of a union type and accesses different attributes depending on the type. The type of the object is specified by a type attribute.

from typing import Union

from dataclasses import dataclass


TEXT = 'TEXT'
IMAGE = 'IMAGE'


@dataclass
class TextMessage:
    text: str
    type = TEXT


@dataclass
class ImageMessage:
    url: str
    type = IMAGE


def output(message: Union[TextMessage, ImageMessage]) -> None:
    if message.type == TEXT:
        print(message.text)

Mypy does not like this, saying:

 error: Item "ImageMessage" of "Union[TextMessage, ImageMessage]" has no attribute "text"

And I understand it. I am trying to guess one attribute based on the value of another one. This is poor engineering.

However, the same thing works in other languages, for example, in JS Flow:

// @flow

type TextMessage = {
	type: 'TEXT',
  	text: string
};
type ImageMessage = {
	type: 'IMAGE',
  	url: string
};

function output(message: TextMessage | ImageMessage) {
  if (message.type === 'TEXT') {
    console.log(message.text);
  }
} 

Live Flow link: here

So the question is, is there a way to make mypy understand my conditions? Maybe using Literal types? I tried to figure this out but the docs are a bit confusing.

P.S. I can always do if isinstance(message, TextMessage) and this will satisfy mypy, but putting lots of isinstance() in my code seems even more hacky.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions