Reputation: 6075
I am trying to get Apollo to integrate with TypeScript. I have a React Class that looks like the following:
interface Data {
allVendors: Array<VendorType>;
}
class AllVendorsQuery extends Query<Data> {}
const ShowVendors: React.SFC<> = props => {
return (
<AllVendorsQuery query={fetchVendors}>
{({ loading, error, data: { allVendors } }) => {
if (loading) {
return 'Loading...';
}
if (error) {
return `Error! ${error.message}`;
}
return (
allVendors &&
allVendors.map((vendor, index: number) => {
return (
<div key={`${vendor.name}_${index}`}>
#<strong>{vendor.id}</strong>
{vendor.name}
</div>
);
})
);
}}
</AllVendorsQuery>
);
};
export default ShowVendors;
The query is:
export default gql`
query GetVendors {
allVendors {
id
name
}
}
`;
TypeScript is complaining about the fact that [ts] Type 'Data | undefined' has no property 'allVendors' and no string index signature.
which occurs on this line: {({ loading, error, data: { allVendors } })
.
However, if I restructure the code using apollo-connect
rather than the Query component I don't get any complain from TypeScript:
import { graphql, compose, QueryResult } from 'react-apollo';
interface ShowVendorsProps {
data: QueryResult & { allVendors?: VendorType[] };
}
class ShowVendors extends React.Component<ShowVendorsProps> {
render() {
const {
data: { allVendors }
} = this.props;
if (allVendors && allVendors.length > 0) {
return (
<div>
{allVendors.map((vendor, index: number) => {
return (
<div key={`${vendor.name}_${index}`}>
#<strong>{vendor.id}</strong>
{vendor.name}
</div>
);
})}
</div>
);
} else {
return 'Loading';
}
}
}
export default compose(graphql(fetchVendors))(ShowVendors);
What's the difference between the two? How can I rewrite the type for the first statement?
Upvotes: 2
Views: 2863
Reputation: 87
There is a small library and a CLI to generate TypeScript typings for both server (according to your schema) and client (according to your schema and GraphQL documents). It also generates resolvers signature and very customizable.
You can try it here: https://github.com/dotansimha/graphql-code-generator
And there is a blog post here about that package; https://medium.com/the-guild/graphql-code-generator-for-typescript-react-apollo-7b225672588f
The idea behind it was to allow the developer to get the most out of GraphQL and the generated typings, and making it easier to customize the generated output.
It can also generate react-apollo components with typings you need.
Upvotes: 3
Reputation: 11
Since you're destructuring data
with data: { allVendors }
in the first codeblock, TypeScript complains because data
might be undefined, for instance when the data is still loading.
So for TS not complaining you could just destructure after the loading check, with a default value for allVendors
, something like:
interface Data {
allVendors: Array<VendorType>;
}
class AllVendorsQuery extends Query<Data> {}
const ShowVendors: React.SFC<> = props => {
return (
<AllVendorsQuery query={fetchVendors}>
{({ loading, error, data }) => {
if (loading) {
return 'Loading...';
}
if (error) {
return `Error! ${error.message}`;
}
// If data is not undefined, then it sets allVendors accordingly.
// Otherwise it sets it to null (which you check for anyways below)
const {allVendors} = data || {allVendors: null};
return (
allVendors &&
allVendors.map((vendor, index: number) => {
return (
<div key={`${vendor.name}_${index}`}>
#<strong>{vendor.id}</strong>
{vendor.name}
</div>
);
})
);
}}
</AllVendorsQuery>
);
};
export default ShowVendors;
Upvotes: 1