2022/7/13/freezed/Readme-translation-part2

 

<<前のページ(Part1)へ


freezedのライセンスページ

MIT License

Copyright (c) 2020 Remi Rousselet

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Union types and Sealed classes

Coming from other languages, you may be used with features like “union types”/”sealed classes”/pattern matching.

別の言語から来た人なら、”union types” / “sealed classes” / によるpattern matchingに慣れている人もいるかもしれません。

 

These are powerful tools in combination with a type system, but Dart currently does not support them.

これらは型システムと合わせることで強力なツールになりますが、Dartでは現在サポートされていません。

 

But fear not, Freezed supports them, generating a few utilities to help you with those.

しかしFreezedでは自動生成によりこれらの機能をサポートしています。

 

Long story short, in any Freezed class, you can writing multiple constructors:

Freezedのクラスは下記のように複数のコンストラクタを定義することができます。

@freezed
class Union with _$Union {
  const factory Union.data(int value) = Data;
  const factory Union.loading() = Loading;
  const factory Union.error([String? message]) = Error;
}

 

By doing this, our model now can be in different mutually exclusive states.

これで私たちのモデルクラス(Union)は、互いに排他的な異なる状態を表現することができます。

 

In particular, this snippet defines a model Union, and that model has 3 possible states:

特に、このスニペットはUnionモデルクラスを定義し、このモデルは三つの取りうる状態を表現することができます。

  • data
  • loading
  • error

定義したfactoryコンストラクタの右側に意味のある名前をつけたことに注意してください。これらは後で役に立ちます。

 

One thing you may also notice is that with this example, then we can no-longer write code such as:

また、この例では、もう次のようなコードは書けないことにお気づきでしょう。

void main() {
  Union union = Union.data(42);

  print(union.value); // compilation error: property value does not exist
}

Let’s see why that is the case in the following section.

その理由を次節で見ていきましょう。


Shared properties

When defining multiple constructors, you will lose the ability to read properties that are not common to all constructors:

複数のコンストラクタを定義したとき、全てのコンストラクタに共通でないプロパティを読み取る能力を失います。

 

For example, if you write:

例えば下記のように書いたとして、

@freezed
class Example with _$Example {
  const factory Example.person(String name, int age) = Person;
  const factory Example.city(String name, int population) = City;
}

 

Then you will be unable to read age and population directly:

この時、ageとpopulationを直接読み取ることはできません。

var example = Example.person('Remi', 24);
print(example.age); // does not compile!

 

On the other hand, you can read properties that are defined on all constructors.
For example, the name variable is common to both Example.person and Example.city constructors.

一方、全てのコンストラクタで宣言されたプロパティは読み取ることができます。例えば、nameはExample.personコンストラクタとExample.cityコンストラクタに共通のものです。

 

As such we can write:

ということで、下記のように書くことができます。

var example = Example.person('Remi', 24);
print(example.name); // Remi
example = Example.city('London', 8900000);
print(example.name); // London

 

The same logic can be applied to copyWith too.
We can use copyWith with properties defined on all constructors:

同じロジックがcopyWithにも当てはまります。

私たちはcoptyWithに全てのコンストラクタで宣言されたプロパティと共に使うことができます。

var example = Example.person('Remi', 24);
print(example.copyWith(name: 'Dash')); // Example.person(name: Dash, age: 24)

example = Example.city('London', 8900000);
print(example.copyWith(name: 'Paris')); // Example.city(name: Paris, population: 8900000)

 

On the other hand, properties that are unique to a specific constructor aren’t available:

一方で、特定のコンストラクタのみが持つプロパティはcopyWithでも使えません。

var example = Example.person('Remi', 24);

example.copyWith(age: 42); 
// compilation error, parameter `age` does not exist
// コンパイルエラー、ageは存在しません。

To solve this problem, we need check the state of our object using what we call “pattern matching”.

この問題を解決するために、私たちは”pattern matching”と呼ばれる方法でオブジェクトの状態を確認する必要があります。


Using pattern matching to read non-shared properties

For this section, let’s consider the following union:

このセクションでは、下記のunionについて考えていきます。

@freezed
class Example with _$Example {
  const factory Example.person(String name, int age) = Person;
  const factory Example.city(String name, int population) = City;
}

 

Let’s see how we can use pattern matching to read the content of an Example instance.

Exampleインスタンスのコンテンツを読み取るためにpattern matchingをどのように使うか見ていきましょう。

 

For this, we have a few solutions:

解決法はいくつかあります。

  • (preferred) Using the utilities (when/map) generated by Freezed to inspect the content of our object

(おすすめ) オブジェクトのコンテンツを確認するためにFreezedで生成された( when / map )を使う。

 

  • (discouraged) Using is/as to cast an Example variable into either a Person or a City

(おすすめできない) ExampleをPerson、Cityにキャストするためにis/asを使う。


When

The when method is the equivalent to pattern matching with destructing.
The prototype of the method depends on the constructors defined.

For example, with:

whenメソッド

工事中🏗

@freezed
class Union with _$Union {
  const factory Union(int value) = Data;
  const factory Union.loading() = Loading;
  const factory Union.error([String? message]) = ErrorDetails;
}

 

Then when will be:

whenメソッドは下記のようになります。

var union = Union(42);

print(
  union.when(
    (int value) => 'Data $value',
    loading: () => 'loading',
    error: (String? message) => 'Error: $message',
  ),
); 

 

Whereas if we defined:

それに対して、下記のように定義した場合は、

@freezed
class Model with _$Model {
  factory Model.first(String a) = First;
  factory Model.second(int b, bool c) = Second;
}

 

Then when will be:

whenメソッドは下記のようになります。

var model = Model.first('42');

print(
  model.when(
    first: (String a) => 'first $a',
    second: (int b, bool c) => 'second $b $c'
  ),
);

 

Notice how each callback matches with a constructor’s name and prototype.

各コールバックがコンストラクタの名前とプロトタイプにマッチしていることに注目してください。

 

NOTE:
All callbacks are required and must not be null.
If that is not what you want, consider using maybeWhen.

注意:

全てのコールバックが必須で、nullにはできません。

そうしたくない場合は、maybeWhenメソッドを使うことを考えてください。


Map

The map methods are equivalent to when, but without destructuring.

mapメソッドはwhenと同じですが、destructuringがありません。

 

@freezed
class Model with _$Model {
  factory Model.first(String a) = First;
  factory Model.second(int b, bool c) = Second;
}

 

With such class, while when will be:

上記のクラスでwhenメソッドは下記のようになりますが、

var model = Model.first('42');

print(
  model.when(
    first: (String a) => 'first $a',
    second: (int b, bool c) => 'second $b $c'
  ),
); // first 42

 

map will instead be:

mapメソッドの場合は下記のようになります。

var model = Model.first('42');

print(
  model.map(
    first: (First value) => 'first ${value.a}',
    second: (Second value) => 'second ${value.b} ${value.c}'
  ),
); // first 42

 

This can be useful if you want to do complex operations, like copyWith/toString for example:

これは、例えばcopyWith/toStringのような複雑な操作を行いたい場合に有効です。

var model = Model.second(42, false)
print(
  model.map(
    first: (value) => value,
    second: (value) => value.copyWith(c: true),
  )
); // Model.second(b: 42, c: true)

 


Using is/as to read the content of a Freezed class

Alternatively, one (less desirable) solution is to use the is/as keywords.
More specifically, you can write:

あるいは、(あまり望ましくありませんが)is/asキーワードを使うという解決策もあります。
具体的には、次のように書きます。

void main() {
  Example value;

  if (value is Person) {
    // By using `is`, this allows the compiler to know that "value" is a Person instance
    // and therefore allows us to read all of its properties.
    // isを使うと、コンパイラに対して、valueがPersonインスタンスであることを知らせることになるので、
    // valueの全てのプロパティを読み取ることが可能になります。
    print(value.age);
    value = value.copyWith(age: 42);
  }

  // Alternatively we can use `as` if we are certain of type of an object:
  // 代わりに、オブジェクトのタイプがわかっている時はasを使うことができます。
  Person person = value as Person;
  print(person.age);
}

 

Using is and as, while possible, is discouraged.

isやasを使うことは可能ですが、あまりおすすめできません。

 

The reasoning is that they are not “exhaustive”. See https://www.fullstory.com/blog/discriminated-unions-and-exhaustiveness-checking-in-typescript/

理由についてはそれらが排他的ではないからです。下記の記事を参考にしてください。

工事中🏗

 

 

参考

#

コメントを残す

メールアドレスが公開されることはありません。