From e371bb125a0b434c0f1a3a70882e2c70a1291bad Mon Sep 17 00:00:00 2001 From: Michael Wiencek Date: Fri, 16 Mar 2018 21:20:18 -0500 Subject: [PATCH] Use object type spreads instead of intersections The previous code is wrong because an object can't simultaneously satisfy two distinct, exact object types. The correct way is using object type spreads as described here: https://github.com/facebook/flow/issues/2626 I'm not sure why things currently type-check. --- root/components/Aliases/AliasTable.js | 2 +- root/components/EntityHeader.js | 2 +- root/components/EntityTabLink.js | 2 +- root/components/EntityTabs.js | 4 +- .../scripts/common/components/CodeLink.js | 1 + .../scripts/common/utility/entityHref.js | 7 +- root/types.js | 322 +++++++++--------- 7 files changed, 182 insertions(+), 158 deletions(-) diff --git a/root/components/Aliases/AliasTable.js b/root/components/Aliases/AliasTable.js index 39eb0a47850..bc731c99e32 100644 --- a/root/components/Aliases/AliasTable.js +++ b/root/components/Aliases/AliasTable.js @@ -14,7 +14,7 @@ const {l} = require('../../static/scripts/common/i18n'); type Props = { +aliases: $ReadOnlyArray, +allowEditing: boolean, - +entity: $Subtype, + +entity: CoreEntityT, }; const AliasTable = (props: Props) => ( diff --git a/root/components/EntityHeader.js b/root/components/EntityHeader.js index ece5b9f6cd3..5589dbaa1d5 100644 --- a/root/components/EntityHeader.js +++ b/root/components/EntityHeader.js @@ -16,7 +16,7 @@ const SubHeader = require('./SubHeader'); type Props = {| +editTab?: React.Node, - +entity: $Subtype, + +entity: CoreEntityT, +headerClass: string, +heading?: string | React.Node, +page: string, diff --git a/root/components/EntityTabLink.js b/root/components/EntityTabLink.js index 02e982b0724..7746ab4d7fb 100644 --- a/root/components/EntityTabLink.js +++ b/root/components/EntityTabLink.js @@ -12,7 +12,7 @@ const EntityLink = require('../static/scripts/common/components/EntityLink'); type Props = {| +content: string, - +entity: EntityT, + +entity: CoreEntityT, +selected: boolean, +subPath: string, |}; diff --git a/root/components/EntityTabs.js b/root/components/EntityTabs.js index 1f84f45beca..75748e1c268 100644 --- a/root/components/EntityTabs.js +++ b/root/components/EntityTabs.js @@ -47,7 +47,7 @@ const buildLink = ( ); function buildLinks( - entity: $Subtype, + entity: CoreEntityT, page: string, editTab: React.Node, hideEditTab: boolean, @@ -91,7 +91,7 @@ function buildLinks( type Props = {| +editTab: React.Node, - +entity: EntityT, + +entity: CoreEntityT, +hideEditTab?: boolean, +page: string, |}; diff --git a/root/static/scripts/common/components/CodeLink.js b/root/static/scripts/common/components/CodeLink.js index e843674be07..eaada43d614 100644 --- a/root/static/scripts/common/components/CodeLink.js +++ b/root/static/scripts/common/components/CodeLink.js @@ -19,6 +19,7 @@ const CodeLink = ({code}: Props) => { let link = ( + {/* $FlowFixMe */} {code[code.entityType]} diff --git a/root/static/scripts/common/utility/entityHref.js b/root/static/scripts/common/utility/entityHref.js index 6c3c7648a3f..eb80f7967e6 100644 --- a/root/static/scripts/common/utility/entityHref.js +++ b/root/static/scripts/common/utility/entityHref.js @@ -14,8 +14,13 @@ const nonEmpty = require('./nonEmpty'); const leadingSlash = /^\/?(.*)/; +type LinkableEntity = + | CoreEntityT + | IsrcT + | IswcT; + function entityHref( - entity: EntityT | CoreEntityT, + entity: LinkableEntity, subPath?: string, ) { const entityType = entity.entityType; diff --git a/root/types.js b/root/types.js index 75c603a7b4e..1c15cc82643 100644 --- a/root/types.js +++ b/root/types.js @@ -16,30 +16,28 @@ * how data is serialized for us. */ -declare type AliasT = - & DatePeriodRoleT - & EntityT - & EditableRoleT - & TypeRoleT - & {| - +entityType: 'alias', - +locale: string | null, - +name: string, - +primary_for_locale: boolean, - +sort_name: string, - |}; +declare type AliasT = {| + ...DatePeriodRoleT, + ...EditableRoleT, + ...EntityRoleT, + ...TypeRoleT, + +entityType: 'alias', + +locale: string | null, + +name: string, + +primary_for_locale: boolean, + +sort_name: string, +|}; export opaque type AliasTypeT: OptionTreeT = OptionTreeT; -declare type AreaT = - & CommentRoleT - & CoreEntityT - & DatePeriodRoleT - & TypeRoleT - & {| - +containment: $ReadOnlyArray, - +entityType: 'area', - |}; +declare type AreaT = {| + ...CommentRoleT, + ...CoreEntityRoleT, + ...DatePeriodRoleT, + ...TypeRoleT, + +containment: $ReadOnlyArray, + +entityType: 'area', +|}; export opaque type AreaTypeT: OptionTreeT = OptionTreeT; @@ -55,14 +53,14 @@ declare type ArtistCreditRoleT = {| declare type ArtistCreditT = $ReadOnlyArray; -declare type ArtistT = - & CommentRoleT - & CoreEntityT - & TypeRoleT - & {| - +entityType: 'artist', - +sort_name: string, - |}; +declare type ArtistT = {| + ...CommentRoleT, + ...CoreEntityRoleT, + ...RatableRoleT, + ...TypeRoleT, + +entityType: 'artist', + +sort_name: string, +|}; export opaque type ArtistTypeT: OptionTreeT = OptionTreeT; @@ -115,11 +113,26 @@ declare type CompoundFieldT = {| +id: number, |}; -declare type CoreEntityT = EntityT & {| +declare type CoreEntityRoleT = {| + ...EntityRoleT, +gid: string, +name: string, |}; +declare type CoreEntityT = + | AreaT + | ArtistT + | EventT + | InstrumentT + | LabelT + | PlaceT + | RecordingT + | ReleaseGroupT + | ReleaseT + | SeriesT + | UrlT + | WorkT; + declare type DatePeriodRoleT = {| +begin_date: PartialDateT | null, +end_date: PartialDateT | null, @@ -130,7 +143,7 @@ declare type EditableRoleT = {| +editsPending: boolean, |}; -declare type EntityT = {| +declare type EntityRoleT = {| +entityType: string, +id: number, |}; @@ -161,51 +174,48 @@ declare type GroupedOptionsT = $ReadOnlyArray<{| |}>, |}>; -declare type InstrumentT = - & CommentRoleT - & CoreEntityT - & TypeRoleT - & {| - +description: string, - +entityType: 'instrument', - |}; +declare type InstrumentT = {| + ...CommentRoleT, + ...CoreEntityRoleT, + ...TypeRoleT, + +description: string, + +entityType: 'instrument', +|}; export opaque type InstrumentTypeT: OptionTreeT = OptionTreeT; -declare type EventT = - & CommentRoleT - & CoreEntityT - & TypeRoleT - & {| - +entityType: 'event', - |}; +declare type EventT = {| + ...CommentRoleT, + ...CoreEntityRoleT, + ...RatableRoleT, + ...TypeRoleT, + +entityType: 'event', +|}; export opaque type EventTypeT: OptionTreeT = OptionTreeT; -declare type IsrcT = - & EditableRoleT - & EntityT - & {| - +entityType: 'isrc', - +isrc: string, - +recording_id: number, - |}; - -declare type IswcT = - & EditableRoleT - & EntityT - & {| - +entityType: 'iswc', - +iswc: string, - +work_id: number, - |}; - -declare type LabelT = - & CommentRoleT - & CoreEntityT - & {| - +entityType: 'label', - |}; +declare type IsrcT = {| + ...EditableRoleT, + ...EntityRoleT, + +entityType: 'isrc', + +isrc: string, + +recording_id: number, +|}; + +declare type IswcT = {| + ...EditableRoleT, + ...EntityRoleT, + +entityType: 'iswc', + +iswc: string, + +work_id: number, +|}; + +declare type LabelT = {| + ...CommentRoleT, + ...CoreEntityRoleT, + ...RatableRoleT, + +entityType: 'label', +|}; declare type LinkTypeAttrTypeT = {| attribute: AttrInfoT, @@ -237,15 +247,14 @@ declare type OptionListT = $ReadOnlyArray<{| +text: string, |}>; -declare type OptionTreeT = - & EntityT - & {| - +gid: string, - +name: string, - +parentID: number | null, - +childOrder: number, - +description: string, - |}; +declare type OptionTreeT = {| + ...EntityRoleT, + +gid: string, + +name: string, + +parentID: number | null, + +childOrder: number, + +description: string, +|}; declare type PartialDateT = {| +day: number | null, @@ -253,52 +262,58 @@ declare type PartialDateT = {| +year: number | null, |}; -declare type PlaceT = - & CommentRoleT - & CoreEntityT - & TypeRoleT - & {| - +entityType: 'place', - |}; +declare type PlaceT = {| + ...CommentRoleT, + ...CoreEntityRoleT, + ...TypeRoleT, + +entityType: 'place', +|}; export opaque type PlaceTypeT: OptionTreeT = OptionTreeT; -declare type RatableT = CoreEntityT & {| +declare type RatableRoleT = {| +rating: number | null, +rating_count: number, +user_rating: number | null, |}; -declare type RecordingT = - & CommentRoleT - & ArtistCreditRoleT - & CoreEntityT - & {| - +entityType: 'recording', - +isrcs: $ReadOnlyArray, - +length: number, - +video: boolean, - |}; - -declare type ReleaseGroupT = - & CommentRoleT - & ArtistCreditRoleT - & CoreEntityT - & {| - +entityType: 'release_group', - |}; - -declare type ReleaseT = - & CommentRoleT - & CoreEntityT - & {| - +barcode: string | null, - +entityType: 'release', - +languageID: number | null, - +packagingID: number | null, - +scriptID: number | null, - +statusID: number | null, - |}; +declare type RatableT = + | ArtistT + | EventT + | LabelT + | RecordingT + | ReleaseGroupT + | WorkT; + +declare type RecordingT = {| + ...ArtistCreditRoleT, + ...CommentRoleT, + ...CoreEntityRoleT, + ...RatableRoleT, + +entityType: 'recording', + +isrcs: $ReadOnlyArray, + +length: number, + +video: boolean, +|}; + +declare type ReleaseGroupT = {| + ...ArtistCreditRoleT, + ...CommentRoleT, + ...CoreEntityRoleT, + ...RatableRoleT, + +entityType: 'release_group', +|}; + +declare type ReleaseT = {| + ...CommentRoleT, + ...CoreEntityRoleT, + +barcode: string | null, + +entityType: 'release', + +languageID: number | null, + +packagingID: number | null, + +scriptID: number | null, + +statusID: number | null, +|}; declare type RepeatableFieldT = {| +errors: $ReadOnlyArray, @@ -307,25 +322,23 @@ declare type RepeatableFieldT = {| +id: number, |}; -declare type SeriesT = - & CommentRoleT - & CoreEntityT - & {| - +entityType: 'series', - |}; +declare type SeriesT = {| + ...CommentRoleT, + ...CoreEntityRoleT, + +entityType: 'series', +|}; declare type TypeRoleT = {| +typeID: number | null, +typeName?: string, |}; -declare type UrlT = - & CoreEntityT - & EditableRoleT - & {| - +decoded: string, - +entityType: 'url', - |}; +declare type UrlT = {| + ...CoreEntityRoleT, + ...EditableRoleT, + +decoded: string, + +entityType: 'url', +|}; declare type UserTagT = {| +count: number, @@ -333,39 +346,44 @@ declare type UserTagT = {| +vote: 1 | -1, |}; -declare type WorkT = - & CommentRoleT - & CoreEntityT - & TypeRoleT - & {| - +entityType: 'work', - |}; +declare type WorkT = {| + ...CommentRoleT, + ...CoreEntityRoleT, + ...TypeRoleT, + ...RatableRoleT, + +entityType: 'work', +|}; export opaque type WorkTypeT: OptionTreeT = OptionTreeT; -declare type WorkAttributeTypeAllowedValueT = - & EntityT - & OptionTreeT - & {|+workAttributeTypeID: number, +value: string|}; +declare type WorkAttributeTypeAllowedValueT = {| + ...EntityRoleT, + ...OptionTreeT, + +workAttributeTypeID: number, + +value: string, +|}; // See MusicBrainz::Server::Controller::Work::stash_work_form_json -declare type WorkAttributeTypeAllowedValueTreeT = - & WorkAttributeTypeAllowedValueT - & {|+children?: $ReadOnlyArray|}; +declare type WorkAttributeTypeAllowedValueTreeT = {| + ...WorkAttributeTypeAllowedValueT, + +children?: $ReadOnlyArray, +|}; declare type WorkAttributeTypeAllowedValueTreeRootT = {|+children: $ReadOnlyArray|}; -declare type WorkAttributeTypeT = - & CommentRoleT - & EntityT - & OptionTreeT - & {|+freeText: boolean|}; +declare type WorkAttributeTypeT = {| + ...CommentRoleT, + ...EntityRoleT, + ...OptionTreeT, + +freeText: boolean, +|}; // See MusicBrainz::Server::Controller::Work::stash_work_form_json -declare type WorkAttributeTypeTreeT = - & WorkAttributeTypeT - & {|+children?: $ReadOnlyArray|}; +declare type WorkAttributeTypeTreeT = {| + ...WorkAttributeTypeT, + +children?: $ReadOnlyArray, +|}; declare type WorkAttributeTypeTreeRootT = {|+children: $ReadOnlyArray|};