应用 apollo 的 graphql() HOC 后由于缺少数据属性而导致的流类型错误
Flow type errors due to missing data prop after applying apollo's graphql() HOC
读完 post about how easy it is to add flow types to a graphql
infused object I figured that I'll give it a try. Since that post in June Flow has had a major upgrade to 0.53 and I can't get the basic example to work with a class component. I'm not sure if it's the upgrade or something in the original definition from react-apollo 后发现不合适。
问题:如何让 class 版本的组件工作?
这是我的示例代码:
// @flow
import * as React from 'react';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
import type { OperationComponent, QueryOpts } from 'react-apollo';
const HERO_QUERY = gql`
query GetCharacter($episode: Episode!) {
hero(episode: $episode) {
name
id
friends {
name
id
appearsIn
}
}
}
`;
type Hero = {
name: string,
id: string,
appearsIn: string[],
friends: Hero[]
};
type Response = {
hero: Hero
};
type Props = {
data: QueryOpts & Response,
}
const withCharacter: OperationComponent<Response, Props> = graphql(HERO_QUERY, {
options: () => ({
variables: { episode: 'JEDI' },
}),
});
class TestClass extends React.Component<Props> {
render() {
const { loading, error, hero } = this.props.data;
if (loading) return <div>Loading</div>;
if (error) return <h1>ERROR</h1>;
return <div>My hero: {hero.name}</div>;
}
}
const TestFn = ({ data: { loading, error, hero } }) => {
if (loading) return <div>Loading</div>;
if (error) return <h1>ERROR</h1>;
return <div>My hero: {hero.name}</div>;
};
export const clss = withCharacter(TestClass); // ERROR!
export const fn = withCharacter(TestFn); // Works fine
这是 OperationComponent
类型的定义,它是 definition 的核心部分,并更新以匹配 0.53 Flow 样式:
export interface QueryProps {
error?: ApolloError,
networkStatus: number,
loading: boolean,
variables: Object,
fetchMore: (
fetchMoreOptions: FetchMoreQueryOptions & FetchMoreOptions,
) => Promise<ApolloQueryResult<any>>,
refetch: (variables?: Object) => Promise<ApolloQueryResult<any>>,
startPolling: (pollInterval: number) => void,
stopPolling: () => void,
subscribeToMore: (options: SubscribeToMoreOptions) => () => void,
updateQuery: (
mapFn: (previousQueryResult: any, options: UpdateQueryOptions) => any,
) => void,
}
export type MutationFunc<TResult> = (
opts: MutationOpts,
) => Promise<ApolloQueryResult<TResult>>;
export type ChildProps<P, R> = {
data: QueryProps & R,
mutate: MutationFunc<R>,
} & P;
export interface OperationComponent<
TResult: Object = {},
TOwnProps: Object = {},
TMergedProps = ChildProps<TOwnProps, TResult>,
> {
(
component:
| StatelessComponent<TMergedProps>
| Class<React$Component<any, TMergedProps, any>>,
): Class<React$Component<TOwnProps, void>>,
}
declare export function graphql<TResult, TProps, TChildProps>(
document: DocumentNode,
operationOptions?: OperationOption<TProps, TResult>,
): OperationComponent<TResult, TProps, TChildProps>;
当我 运行 flow focus-check src/test.js
(v. 0.53.1) 我得到:
Error: src/test.js:42
42: const { loading, error, hero } = this.props.data;
^^^^^^^ property `loading`. Property cannot be accessed on any member of intersection type
42: const { loading, error, hero } = this.props.data;
^^^^^^^^^^^^^^^ intersection
Member 1:
31: data: QueryOpts & Response,
^^^^^^^^^ QueryOpts
Error:
42: const { loading, error, hero } = this.props.data;
^^^^^^^ property `loading`. Property not found in
31: data: QueryOpts & Response,
^^^^^^^^^ QueryOpts
Member 2:
31: data: QueryOpts & Response,
^^^^^^^^ Response
Error:
42: const { loading, error, hero } = this.props.data;
^^^^^^^ property `loading`. Property not found in
31: data: QueryOpts & Response,
^^^^^^^^ object type
Error: src/test.js:42
42: const { loading, error, hero } = this.props.data;
^^^^^ property `error`. Property cannot be accessed on any member of intersection type
42: const { loading, error, hero } = this.props.data;
^^^^^^^^^^^^^^^ intersection
Member 1:
31: data: QueryOpts & Response,
^^^^^^^^^ QueryOpts
Error:
42: const { loading, error, hero } = this.props.data;
^^^^^ property `error`. Property not found in
31: data: QueryOpts & Response,
^^^^^^^^^ QueryOpts
Member 2:
31: data: QueryOpts & Response,
^^^^^^^^ Response
Error:
42: const { loading, error, hero } = this.props.data;
^^^^^ property `error`. Property not found in
31: data: QueryOpts & Response,
^^^^^^^^ object type
Error: src/test.js:55
55: export const clss = withCharacter(TestClass);
^^^^^^^^^ class type: TestClass. This type is incompatible with
v-------------------------------
123: | StatelessComponent<TMergedProps>
124: | Class<React$Component<any, TMergedProps, any>>,
-----------------------------------------------^ union: type application of polymorphic type: type `StatelessComponent` | class type: type application of identifier `React$Component`. See: node_modules/react-apollo/react-apollo.umd.js.flow:123
Member 1:
123: | StatelessComponent<TMergedProps>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type application of polymorphic type: type `StatelessComponent`. See: node_modules/react-apollo/react-apollo.umd.js.flow:123
Error:
123: | StatelessComponent<TMergedProps>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function type. Callable signature not found in. See: node_modules/react-apollo/react-apollo.umd.js.flow:123
55: export const clss = withCharacter(TestClass);
^^^^^^^^^ statics of TestClass
Member 2:
124: | Class<React$Component<any, TMergedProps, any>>,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ class type: type application of identifier `React$Component`. See: node_modules/react-apollo/react-apollo.umd.js.flow:124
Error:
124: | Class<React$Component<any, TMergedProps, any>>,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type application of identifier `React$Component`. Too many type arguments. Expected at most 2. See: node_modules/react-apollo/react-apollo.umd.js.flow:124
29: declare class React$Component<Props, State = void> {
^^^^^^^^^^^^ See type parameters of definition here. See lib: /tmp/flow/flowlib_1135e841/react.js:29
Found 3 errors
我尝试添加一些 0.53 的改进但没有用:
export interface OperationComponent<
TResult: Object = {},
TOwnProps: Object = {},
TMergedProps = ChildProps<TOwnProps, TResult>,
> {
(
component: React$ComponentType<TMergedProps>
): React$ComponentType<TOwnProps>,
}
哦...错误的查询,应该是QueryProps
而不是QueryOpts
:
// @flow
import * as React from 'react';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
import type { OperationComponent, QueryProps } from 'react-apollo';
const HERO_QUERY = gql`
query GetCharacter($episode: Episode!) {
hero(episode: $episode) {
name
id
friends {
name
id
appearsIn
}
}
}
`;
type Hero = {
name: string,
id: string,
appearsIn: string[],
friends: Hero[]
};
type Response = {
hero: Hero
};
type Props = {
data: QueryProps & Response,
mutate: any,
}
const withCharacter: OperationComponent<Response, Props> = graphql(HERO_QUERY, {
options: () => ({
variables: { episode: 'JEDI' },
}),
});
class TestClass extends React.Component<Props> {
render() {
const { loading, error, hero } = this.props.data;
if (loading) return <div>Loading</div>;
if (error) return <h1>ERROR</h1>;
return <div>My hero: {hero.name}</div>;
}
}
const TestFn = ({ data: { loading, error, hero } }) => {
if (loading) return <div>Loading</div>;
if (error) return <h1>ERROR</h1>;
return <div>My hero: {hero.name}</div>;
};
export const clss = withCharacter(TestClass); // Works
export const fn = withCharacter(TestFn); // Works
读完 post about how easy it is to add flow types to a graphql
infused object I figured that I'll give it a try. Since that post in June Flow has had a major upgrade to 0.53 and I can't get the basic example to work with a class component. I'm not sure if it's the upgrade or something in the original definition from react-apollo 后发现不合适。
问题:如何让 class 版本的组件工作?
这是我的示例代码:
// @flow
import * as React from 'react';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
import type { OperationComponent, QueryOpts } from 'react-apollo';
const HERO_QUERY = gql`
query GetCharacter($episode: Episode!) {
hero(episode: $episode) {
name
id
friends {
name
id
appearsIn
}
}
}
`;
type Hero = {
name: string,
id: string,
appearsIn: string[],
friends: Hero[]
};
type Response = {
hero: Hero
};
type Props = {
data: QueryOpts & Response,
}
const withCharacter: OperationComponent<Response, Props> = graphql(HERO_QUERY, {
options: () => ({
variables: { episode: 'JEDI' },
}),
});
class TestClass extends React.Component<Props> {
render() {
const { loading, error, hero } = this.props.data;
if (loading) return <div>Loading</div>;
if (error) return <h1>ERROR</h1>;
return <div>My hero: {hero.name}</div>;
}
}
const TestFn = ({ data: { loading, error, hero } }) => {
if (loading) return <div>Loading</div>;
if (error) return <h1>ERROR</h1>;
return <div>My hero: {hero.name}</div>;
};
export const clss = withCharacter(TestClass); // ERROR!
export const fn = withCharacter(TestFn); // Works fine
这是 OperationComponent
类型的定义,它是 definition 的核心部分,并更新以匹配 0.53 Flow 样式:
export interface QueryProps {
error?: ApolloError,
networkStatus: number,
loading: boolean,
variables: Object,
fetchMore: (
fetchMoreOptions: FetchMoreQueryOptions & FetchMoreOptions,
) => Promise<ApolloQueryResult<any>>,
refetch: (variables?: Object) => Promise<ApolloQueryResult<any>>,
startPolling: (pollInterval: number) => void,
stopPolling: () => void,
subscribeToMore: (options: SubscribeToMoreOptions) => () => void,
updateQuery: (
mapFn: (previousQueryResult: any, options: UpdateQueryOptions) => any,
) => void,
}
export type MutationFunc<TResult> = (
opts: MutationOpts,
) => Promise<ApolloQueryResult<TResult>>;
export type ChildProps<P, R> = {
data: QueryProps & R,
mutate: MutationFunc<R>,
} & P;
export interface OperationComponent<
TResult: Object = {},
TOwnProps: Object = {},
TMergedProps = ChildProps<TOwnProps, TResult>,
> {
(
component:
| StatelessComponent<TMergedProps>
| Class<React$Component<any, TMergedProps, any>>,
): Class<React$Component<TOwnProps, void>>,
}
declare export function graphql<TResult, TProps, TChildProps>(
document: DocumentNode,
operationOptions?: OperationOption<TProps, TResult>,
): OperationComponent<TResult, TProps, TChildProps>;
当我 运行 flow focus-check src/test.js
(v. 0.53.1) 我得到:
Error: src/test.js:42
42: const { loading, error, hero } = this.props.data;
^^^^^^^ property `loading`. Property cannot be accessed on any member of intersection type
42: const { loading, error, hero } = this.props.data;
^^^^^^^^^^^^^^^ intersection
Member 1:
31: data: QueryOpts & Response,
^^^^^^^^^ QueryOpts
Error:
42: const { loading, error, hero } = this.props.data;
^^^^^^^ property `loading`. Property not found in
31: data: QueryOpts & Response,
^^^^^^^^^ QueryOpts
Member 2:
31: data: QueryOpts & Response,
^^^^^^^^ Response
Error:
42: const { loading, error, hero } = this.props.data;
^^^^^^^ property `loading`. Property not found in
31: data: QueryOpts & Response,
^^^^^^^^ object type
Error: src/test.js:42
42: const { loading, error, hero } = this.props.data;
^^^^^ property `error`. Property cannot be accessed on any member of intersection type
42: const { loading, error, hero } = this.props.data;
^^^^^^^^^^^^^^^ intersection
Member 1:
31: data: QueryOpts & Response,
^^^^^^^^^ QueryOpts
Error:
42: const { loading, error, hero } = this.props.data;
^^^^^ property `error`. Property not found in
31: data: QueryOpts & Response,
^^^^^^^^^ QueryOpts
Member 2:
31: data: QueryOpts & Response,
^^^^^^^^ Response
Error:
42: const { loading, error, hero } = this.props.data;
^^^^^ property `error`. Property not found in
31: data: QueryOpts & Response,
^^^^^^^^ object type
Error: src/test.js:55
55: export const clss = withCharacter(TestClass);
^^^^^^^^^ class type: TestClass. This type is incompatible with
v-------------------------------
123: | StatelessComponent<TMergedProps>
124: | Class<React$Component<any, TMergedProps, any>>,
-----------------------------------------------^ union: type application of polymorphic type: type `StatelessComponent` | class type: type application of identifier `React$Component`. See: node_modules/react-apollo/react-apollo.umd.js.flow:123
Member 1:
123: | StatelessComponent<TMergedProps>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type application of polymorphic type: type `StatelessComponent`. See: node_modules/react-apollo/react-apollo.umd.js.flow:123
Error:
123: | StatelessComponent<TMergedProps>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function type. Callable signature not found in. See: node_modules/react-apollo/react-apollo.umd.js.flow:123
55: export const clss = withCharacter(TestClass);
^^^^^^^^^ statics of TestClass
Member 2:
124: | Class<React$Component<any, TMergedProps, any>>,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ class type: type application of identifier `React$Component`. See: node_modules/react-apollo/react-apollo.umd.js.flow:124
Error:
124: | Class<React$Component<any, TMergedProps, any>>,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type application of identifier `React$Component`. Too many type arguments. Expected at most 2. See: node_modules/react-apollo/react-apollo.umd.js.flow:124
29: declare class React$Component<Props, State = void> {
^^^^^^^^^^^^ See type parameters of definition here. See lib: /tmp/flow/flowlib_1135e841/react.js:29
Found 3 errors
我尝试添加一些 0.53 的改进但没有用:
export interface OperationComponent<
TResult: Object = {},
TOwnProps: Object = {},
TMergedProps = ChildProps<TOwnProps, TResult>,
> {
(
component: React$ComponentType<TMergedProps>
): React$ComponentType<TOwnProps>,
}
哦...错误的查询,应该是QueryProps
而不是QueryOpts
:
// @flow
import * as React from 'react';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
import type { OperationComponent, QueryProps } from 'react-apollo';
const HERO_QUERY = gql`
query GetCharacter($episode: Episode!) {
hero(episode: $episode) {
name
id
friends {
name
id
appearsIn
}
}
}
`;
type Hero = {
name: string,
id: string,
appearsIn: string[],
friends: Hero[]
};
type Response = {
hero: Hero
};
type Props = {
data: QueryProps & Response,
mutate: any,
}
const withCharacter: OperationComponent<Response, Props> = graphql(HERO_QUERY, {
options: () => ({
variables: { episode: 'JEDI' },
}),
});
class TestClass extends React.Component<Props> {
render() {
const { loading, error, hero } = this.props.data;
if (loading) return <div>Loading</div>;
if (error) return <h1>ERROR</h1>;
return <div>My hero: {hero.name}</div>;
}
}
const TestFn = ({ data: { loading, error, hero } }) => {
if (loading) return <div>Loading</div>;
if (error) return <h1>ERROR</h1>;
return <div>My hero: {hero.name}</div>;
};
export const clss = withCharacter(TestClass); // Works
export const fn = withCharacter(TestFn); // Works