在 Flow 中定义子类型的正确方法是什么?

What is the correct way to define a subtype in Flow?

在下面的代码示例中,目的是 scrapeMovies 函数只接受可序列化的对象。

/* @flow */

type SerializableObjectType = {
  [key: string]: string | number | boolean | $ReadOnlyArray<SerializableObjectType> | SerializableObjectType
};

type GuideType = {|
  rid: string
|};

const guide: GuideType = {
  rid: 'foo'
};

const scrapeMovies = async (guide: SerializableObjectType) => {};

scrapeMovies(guide);

https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgDKeATgJYCGMZAXhQEYx4Dy9AVngMYYAquBAXjABvVGDABtANZ4sALjABnDOQB2AcwC6C5WvVgAPmFUBXALb1ShsPThwmFVdYAkAJTwUAJs1UwsAQRISCiwAHmJyKloGJlYObj58AD5rCMpqOkYWdi5eflQAXwBudGx8MABxEzJPPETBEQMxMHJPHRUyDVQDYvROOFVlMHVq2oUqmrr+MCFRcVaFAHIoO0XCktR+wYwlTmD8AFk4ADcyPEUZsApFLFVOMAAKEcmFNKjM2JyE-gBKGZThL1UIo9hRDiczoonqM8D8ikA

我不明白 抱怨 scrapeMovies 被喂食 guide 对象的原因是什么,这是对 SerializableObjectType 的不太严格的定义。

在这里定义子类型的正确方法是什么?

传递 GuideType 代替 SerializableObjectType 是传递更具体类型的一个实例。默认情况下,Flow 仅允许 less specific (contravariant/write-only) 个函数输入。

要使其正常工作,您需要将 guide 参数标记为 covariant. By marking the object as $ReadOnly<T>,您实际上会将其标记为协变:

(Try)

type SerializableObjectType = {
  [key: string]: string | number | boolean | $ReadOnlyArray<SerializableObjectType> | SerializableObjectType
};

type GuideType = {|
  rid: string
|};

const guide: GuideType = {
  rid: 'foo'
};

const scrapeMovies = async (guide: $ReadOnly<SerializableObjectType>) => {};

scrapeMovies(guide);

或者,您可以在对象类型声明中使用 + 符号标记 specific properties as covariant/read-only

(Try)

type SerializableObjectType = {
  +[key: string]: string | number | boolean | $ReadOnlyArray<SerializableObjectType> | SerializableObjectType
};

type GuideType = {|
  rid: string
|};

const guide: GuideType = {
  rid: 'foo'
};

const scrapeMovies = async (guide: SerializableObjectType) => {};

scrapeMovies(guide);

你为什么要跳这个舞?为什么输入是否为 covariant/contravariant/whateverariant 很重要?那么,如果您尝试将 guide 参数视为 SerializeableObjectType(一般对象映射),并且将 number 写入 rid 键,则该参数的其他使用者guide 对象不会得到他们所期望的。