Ошибка потока с глубокими подтипами - PullRequest
0 голосов
/ 03 мая 2018

По Подтипам объектов Документация потока, это работает

// @flow
type ObjectA = { foo: string };
type ObjectB = { foo: string, bar: number };

let objectB: ObjectB = { foo: 'test', bar: 42 };
let objectA: ObjectA = objectB; // Works!

Но более глубокая реализация этого не делает

// @flow
type ObjectA = { foo: { bar: string } };
type ObjectB = { foo: { bar: string, baz: string } };

let objectB: ObjectB = { foo: { bar: '123', baz: '456' } };
let objectA: ObjectA = objectB; // Error! Why?

Есть идеи, почему?

Ответы [ 2 ]

0 голосов
/ 08 мая 2018

На высоком уровне вы можете либо повторно использовать экземпляр objectB с небольшим изменением типа ObjectA, либо создать новый объект. Давайте покопаемся в ваших вариантах:

Пометка свойства объекта как ковариантного

То, что вы пытаетесь сделать, это разыграть ObjectB до ObjectA. Причина, по которой Flow жалуется, состоит в том, что тип foo в ObjectA по умолчанию инвариант . Свойство foo ObjectB является подтипом свойства foo ObjectA. Чтобы Flow понял это, нам просто нужно пометить свойство foo как ковариант :

( Попробуйте )

// @flow
type ObjectA = { +foo: { bar: string } };
type ObjectB = { foo: { bar: string, baz: string } };

let objectB: ObjectB = { foo: { bar: '123', baz: '456' } };
let objectA: ObjectA = objectB; // Woohoo, no error

Пометка свойства как «ковариантного» в основном говорит о том, что вы обещаете читать только это свойство, но не будете писать в него. Смотрите, если вы удалите свойство baz объекта A, оно удалит baz из объекта B. Отметив его как ковариантный, Flow выдаст ошибку, если вы напишите ему:

( Попробуйте )

// @flow
type ObjectA = { +foo: { bar: string } };
type ObjectB = { foo: { bar: string, baz: string } };

let objectB: ObjectB = { foo: { bar: '123', baz: '456' } };
let objectA: ObjectA = objectB;

objectA.foo = {bar: 'oh-oh, deleted baz in objectB'}; //Error

Этот шаблон также работает для объектов с более глубокими вложениями:

( Попробуйте )

// @flow
type ObjectA = { +foo: { +bar: { baz: string } } };
type ObjectB = { foo: { bar: { bax: string, baz: string } } };

let objectB: ObjectB = { foo: { bar: { bax: '123', baz: '456' } } };
let objectA: ObjectA = objectB; // Woohoo, no error

См. Документацию Flow по подтипу глубины для получения более подробной информации об этом наборе.

Использование $ReadOnly<T>

Flow имеет тип utlity, $ReadOnly<T>, чтобы пометить все свойства объекта как ковариантные, поэтому вы можете использовать это вместо:

( Попробуйте )

// @flow
type ObjectA = $ReadOnly<{ foo: { bar: string } }>;
type ObjectB = { foo: { bar: string, baz: string } };

let objectB: ObjectB = { foo: { bar: '123', baz: '456' } };
let objectA: ObjectA = objectB; // Woohoo, no error

Или вы можете создать экземпляр ObjectA ReadOnly и оставить определение ObjectA в покое:

( Попробуйте )

// @flow
type ObjectA = { foo: { bar: string } };
type ObjectB = { foo: { bar: string, baz: string } };

let objectB: ObjectB = { foo: { bar: '123', baz: '456' } };
let objectA: $ReadOnly<ObjectA> = objectB; // Woohoo, no error

Создание нового объекта

В качестве альтернативы, вы можете создать новую копию объекта с разворотом и избежать всего этого:

( Попробуйте )

// @flow
type ObjectA = { foo: { bar: string } };
type ObjectB = { foo: { bar: string, baz: string } };

let objectB: ObjectB = { foo: { bar: '123', baz: '456' } };
let objectA: ObjectA = {...objectB} // Create a new object

Но это сработает только на один уровень и создаст дополнительный объект. Обычно я пропускаю эту опцию и в итоге использую $ReadOnly<T>, когда мне нужно использовать объект только для чтения.

0 голосов
/ 04 мая 2018

Насколько я знаю, один из возможных подходов - это разложить объект. Без этого потока невозможно прочитать свойство.

// @flow
type ObjectA = { foo: { bar: string } };
type ObjectB = { foo: { bar: string, baz: string } };

let objectB: ObjectB = { foo: { bar: '123', baz: '456' } };
let objectA: ObjectA = {...objectB}; // spread operator applied
...