dataclasses-json：オプショナルなプロパティの型を`None | str`にして`schema().load`を実行したらwarningが発生した。

:::note
この問題は[dataclasses-json v0.5.9](https://github.com/lidatong/dataclasses-json/releases/tag/v0.5.9)で解決されました。
:::

# 環境
* Python 3.11.2
* [dataclasses-json](https://github.com/lidatong/dataclasses-json) 0.5.7


# 起きたこと
以下のコードを実行したら`dataclasses_json`で「Unknown type <class 'NoneType'> at Project.project_name: None | str」というwarningが発生しました。


```python:sample1.py
from dataclasses import dataclass
from dataclasses_json import DataClassJsonMixin


@dataclass(frozen=True)
class Project(DataClassJsonMixin):
    project_id: str
    project_name: None | str = None


print("Use `Project.from_dict()`")
print(Project.from_dict({"project_id": "1"}))
print("Use `Project.schema().load()`")
print(Project.schema().load([{"project_id": "2"}], many=True))
```



```
$ python sample1.py 
Use `Project.from_dict()`
Project(project_id='1', project_name=None)
Use `Project.schema().load()`
/home/vagrant/.pyenv/versions/3.11.2/lib/python3.11/site-packages/dataclasses_json/mm.py:271: UserWarning: Unknown type <class 'NoneType'> at Project.project_name: None | str It's advised to pass the correct marshmallow type to `mm_field`.
  warnings.warn(
[Project(project_id='2', project_name=None)]
```


`Project.from_dict()`ではwarningは発生しなかったのですが、`Project.schema().load()`ではwarningが発生しました。


# 解決方法

## `project_name`の型を`Optional[str]`にする

`project_name`の型を`None | str`ではなく`Optional[str]`にすると、`Project.schema().load()`でwanringが発生しませんでした。

```python:sample2.py
from dataclasses import dataclass
from dataclasses_json import DataClassJsonMixin
from typing import Optional


@dataclass(frozen=True)
class Project(DataClassJsonMixin):
    project_id: str
    project_name: Optional[str] = None


print("Use `Project.from_dict()`")
print(Project.from_dict({"project_id": "1"}))
print("Use `Project.schema().load()`")
print(Project.schema().load([{"project_id": "2"}], many=True))
```


```
$ python sample2.py 
Use `Project.from_dict()`
Project(project_id='1', project_name=None)
Use `Project.schema().load()`
[Project(project_id='2', project_name=None)]
```


## `project_name`に`mm_field`を指定する
「It's advised to pass the correct marshmallow type to `mm_field`.」というメッセージ通り、`mm_field`を指定すればwarningが発生しません。


```python:sample.py
from dataclasses_json import DataClassJsonMixin
from marshmallow import fields

from dataclasses import dataclass, field
from dataclasses_json import config


@dataclass(frozen=True)
class Project(DataClassJsonMixin):
    project_id: str
    project_name: None | str = field(
        metadata=config(mm_field=fields.Str(load_default=None))
    )


print("Use `Project.from_dict()`")
print(Project.from_dict({"project_id": "1"}, infer_missing=True))
print("Use `Project.schema().load()`")
print(Project.schema().load([{"project_id": "2"}], many=True))
```

```
$ python sample3.py 
Use `Project.from_dict()`
Project(project_id='1', project_name=None)
Use `Project.schema().load()`
[Project(project_id='2', project_name=None)]
```

ただし`Project.from_dict()`で`infer_missing=True`を指定しないと、エラーが発生します。

https://qiita.com/yuji38kwmt/items/c5b56f70da3b8a70ba31


# まとめ
`dataclass-json`を利用したデータクラスで、オプショナルなプロパティの型は`None | T`より`Optional[T]`の方がよいです。その方が、 dataclasses-jsonとmarshmallow schemaの挙動が（たぶん）同じになるためです。

# 補足
issueを作成しました。~~PRも一緒に作成されたので、そのうち修正されるかもしれません。~~

https://github.com/lidatong/dataclasses-json/issues/407#issuecomment-1585712866

プルリクがマージされました！

https://github.com/lidatong/dataclasses-json/pull/419