Skip to content

Commit 8dcdb92

Browse files
joe-reGuillaume Chau
authored andcommitted
TypeScript support (#160)
* TypeScript support * set defaultOptions key to VueApollo constructor
1 parent c9c3ce4 commit 8dcdb92

File tree

10 files changed

+455
-2
lines changed

10 files changed

+455
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
node_modules/
2+
types/test/*.js

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
src/
2+
types/test/

package.json

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
"main": "dist/vue-apollo.umd.js",
66
"module": "dist/vue-apollo.esm.js",
77
"unpkg": "dist/vue-apollo.min.js",
8+
"typings": "types/index.d.ts",
89
"scripts": {
910
"build": "npm run build:browser && npm run build:es && npm run build:umd",
1011
"build:browser": "rollup --config build/rollup.config.browser.js",
1112
"build:es": "rollup --config build/rollup.config.es.js",
1213
"build:umd": "rollup --config build/rollup.config.umd.js",
1314
"prepublish": "npm run build",
14-
"dev": "npm-watch"
15+
"dev": "npm-watch",
16+
"test:types": "tsc -p types/test"
1517
},
1618
"watch": {
1719
"build": "src/*.js"
@@ -40,6 +42,11 @@
4042
"lodash.throttle": "^4.1.1"
4143
},
4244
"devDependencies": {
45+
"@types/graphql": "^0.11.7",
46+
"apollo-cache-inmemory": "^1.1.1",
47+
"apollo-client": "^2.0.3",
48+
"apollo-link": "^1.0.3",
49+
"apollo-link-http": "^1.2.0",
4350
"babel-core": "^6.26.0",
4451
"babel-eslint": "^7.1.1",
4552
"babel-plugin-external-helpers": "^6.22.0",
@@ -51,6 +58,8 @@
5158
"eslint-plugin-node": "^5.2.1",
5259
"eslint-plugin-promise": "^3.4.0",
5360
"eslint-plugin-standard": "^3.0.1",
61+
"graphql": "^0.11.7",
62+
"graphql-tag": "^2.5.0",
5463
"npm-watch": "^0.3.0",
5564
"rimraf": "^2.6.1",
5665
"rollup": "^0.50.0",
@@ -59,6 +68,8 @@
5968
"rollup-plugin-node-resolve": "^3.0.0",
6069
"rollup-plugin-replace": "^2.0.0",
6170
"rollup-plugin-uglify": "^2.0.1",
62-
"uglify-es": "^3.1.6"
71+
"typescript": "^2.6.2",
72+
"uglify-es": "^3.1.6",
73+
"vue": "^2.5.9"
6374
}
6475
}

types/index.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import './vue'
2+
import { VueApollo } from './vue-apollo';
3+
4+
export default VueApollo;

types/test/App.ts

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// this example src is https://github.com/Akryum/vue-apollo-example
2+
import gql from 'graphql-tag';
3+
import Vue from 'vue';
4+
const pageSize = 10;
5+
const SUB_QUERY = gql`subscription tags($type: String!) {
6+
tagAdded(type: $type) {
7+
id
8+
label
9+
type
10+
}
11+
}`;
12+
export default Vue.extend({
13+
data () {
14+
return {
15+
newTag: null,
16+
updateCount: 0,
17+
type: 'City',
18+
skipQuery: false,
19+
loading: 0,
20+
tagsLoading: 0,
21+
tagsPageLoading: 0,
22+
showTag: 'random',
23+
showMoreEnabled: true,
24+
page: 0,
25+
_type: ''
26+
}
27+
},
28+
apollo: {
29+
$client: 'a',
30+
$loadingKey: 'loading',
31+
tags() {
32+
return {
33+
query: gql`query tagList ($type: String!) {
34+
tags(type: $type) {
35+
id
36+
label
37+
}
38+
}`,
39+
// Reactive variables
40+
variables () {
41+
return {
42+
type: this.type,
43+
};
44+
},
45+
manual: true,
46+
pollInterval: 300,
47+
result (result) {
48+
this.updateCount ++;
49+
},
50+
skip () {
51+
return this.skipQuery
52+
},
53+
fetchPolicy: 'cache-and-network',
54+
subscribeToMore: [{
55+
document: SUB_QUERY,
56+
variables () {
57+
return { type: this.type, }
58+
},
59+
updateQuery: (previousResult, { subscriptionData }) => {
60+
console.log('new tag', subscriptionData.data.tagAdded)
61+
if (previousResult.tags.find((tag: any) => tag.id === subscriptionData.data.tagAdded.id)) {
62+
return previousResult
63+
}
64+
return {
65+
tags: [
66+
...previousResult.tags,
67+
subscriptionData.data.tagAdded,
68+
],
69+
}
70+
},
71+
}],
72+
}
73+
},
74+
randomTag: {
75+
query () {
76+
if (this.showTag === 'random') {
77+
return gql`{
78+
randomTag {
79+
id
80+
label
81+
type
82+
}
83+
}`
84+
} else if (this.showTag === 'last') {
85+
return gql`{
86+
randomTag: lastTag {
87+
id
88+
label
89+
type
90+
}
91+
}`
92+
}
93+
},
94+
},
95+
tagsPage: {
96+
// GraphQL Query
97+
query: gql`query tagsPage ($page: Int!, $pageSize: Int!) {
98+
tagsPage(page: $page, size: $pageSize) {
99+
tags {
100+
id
101+
label
102+
type
103+
}
104+
hasMore
105+
}
106+
}`,
107+
variables: {
108+
page: 0,
109+
pageSize,
110+
},
111+
},
112+
},
113+
methods: {
114+
addTag() {
115+
const newTag = this.newTag;
116+
this.$apollo.mutate({
117+
mutation: gql`mutation ($type: String!, $label: String!) {
118+
addTag(type: $type, label: $label) {
119+
id
120+
label
121+
}
122+
}`,
123+
variables: { type: this.type, label: newTag, },
124+
updateQueries: {
125+
tagList: (previousResult, { mutationResult }) => {
126+
const { data } = mutationResult;
127+
if (!data) { return previousResult }
128+
if (previousResult.tags.find((tag: any) => tag.id === data.addTag.id)) {
129+
return previousResult
130+
}
131+
return { tags: [ ...previousResult.tags, data.addTag ] };
132+
},
133+
},
134+
optimisticResponse: {
135+
__typename: 'Mutation',
136+
addTag: {
137+
__typename: 'Tag',
138+
id: -1,
139+
label: newTag,
140+
type: this.type,
141+
},
142+
},
143+
}).then((data) => {
144+
console.log(data);
145+
}).catch((error) => {
146+
console.error(error);
147+
this.newTag = newTag;
148+
});
149+
},
150+
showMore() {
151+
this.page ++;
152+
this.$apollo.queries.tagsPage.fetchMore({
153+
variables: {
154+
page: this.page,
155+
pageSize,
156+
},
157+
// Mutate the previous result
158+
updateQuery: (previousResult: any, result: { fetchMoreResult: any }) => {
159+
const { fetchMoreResult } = result;
160+
const newTags = fetchMoreResult.tagsPage.tags;
161+
const hasMore = fetchMoreResult.tagsPage.hasMore;
162+
this.showMoreEnabled = hasMore;
163+
return {
164+
tagsPage: {
165+
__typename: previousResult.tagsPage.__typename,
166+
tags: [
167+
...previousResult.tagsPage.tags,
168+
// Add the new tags
169+
...newTags,
170+
],
171+
hasMore,
172+
},
173+
};
174+
},
175+
});
176+
},
177+
refetchTags () {
178+
this.$apollo.queries.tags.refetch()
179+
},
180+
},
181+
mounted() {
182+
const observer = this.$apollo.subscribe({
183+
query: SUB_QUERY,
184+
variables: {
185+
type: 'Companies',
186+
},
187+
});
188+
observer.subscribe({
189+
next(data) {
190+
console.log('this.$apollo.subscribe', data);
191+
},
192+
});
193+
},
194+
});

types/test/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Vue from 'vue'
2+
3+
import 'isomorphic-fetch'
4+
import { ApolloClient } from 'apollo-client'
5+
import { HttpLink } from 'apollo-link-http'
6+
import { ApolloLink, split } from 'apollo-link'
7+
import { getMainDefinition } from 'apollo-utilities'
8+
9+
import VueApollo from '../index'
10+
import App from './App'
11+
12+
const httpLink = new HttpLink({ uri: 'https://dummy.test.com' })
13+
const cache: any = 'dummy cache';
14+
const apolloClient = new ApolloClient({ link: httpLink, cache, connectToDevTools: true })
15+
const apolloProvider = new VueApollo({ defaultClient: apolloClient })
16+
17+
Vue.use(VueApollo)
18+
19+
new Vue({ el: '#app', apolloProvider, render: h => h(App), })

types/test/tsconfig.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es5",
4+
"lib": [
5+
"es5",
6+
"es6",
7+
"dom",
8+
"es2015.core",
9+
"es2015.collection",
10+
"es2015.generator",
11+
"es2015.iterable",
12+
"es2015.promise",
13+
"es2015.proxy",
14+
"es2015.reflect",
15+
"es2015.symbol",
16+
"es2015.symbol.wellknown",
17+
"esnext.asynciterable"
18+
],
19+
"module": "es2015",
20+
"moduleResolution": "node",
21+
"experimentalDecorators": true,
22+
"strict": true
23+
},
24+
"include": [
25+
"*.ts",
26+
"../*.d.ts"
27+
]
28+
}

types/vue-apollo.d.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import Vue, { PluginObject, PluginFunction } from 'vue';
2+
import { DocumentNode } from 'graphql';
3+
import { ApolloClient } from 'apollo-client';
4+
import { WatchQueryOptions, MutationOptions, SubscriptionOptions, SubscribeToMoreOptions, ObservableQuery, NetworkStatus } from 'apollo-client'
5+
import { DataProxy } from 'apollo-cache';
6+
import { subscribe } from 'graphql/subscription/subscribe';
7+
8+
// include Omit type from https://github.com/Microsoft/TypeScript/issues/12215
9+
type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
10+
type Omit<T, K extends keyof T> = { [P in Diff<keyof T, K>]?: T[P] };
11+
12+
type VueApolloOptions = {
13+
$skip?: boolean,
14+
$skipAllQueries?: boolean,
15+
$skipAllSubscriptions?: boolean,
16+
$client?: string,
17+
$loadingKey?: string,
18+
$error?: Function
19+
}
20+
21+
export class VueApollo implements PluginObject<{}> {
22+
[key: string]: any;
23+
install: PluginFunction<{}>;
24+
constructor (options: { defaultClient: ApolloClient<{}>, defaultOptions?: VueApolloOptions });
25+
static install(pVue: typeof Vue, options?:{} | undefined): void;
26+
}
27+
28+
type ApolloVueThisType<V> = V & { [key: string]: any };
29+
type VariableFn<V> = ((this: ApolloVueThisType<V>) => Object) | Object;
30+
type ApolloVueUpdateQueryFn<V> = (this: ApolloVueThisType<V>, previousQueryResult: { [key: string]: any }, options: {
31+
error: any,
32+
subscriptionData: { data: any; };
33+
variables?: { [key: string]: any; };
34+
}) => Object;
35+
36+
interface ApolloVueSubscribeToMoreOptions<V> {
37+
document: DocumentNode;
38+
variables?: VariableFn<V>;
39+
updateQuery?: ApolloVueUpdateQueryFn<V>;
40+
onError?: (error: Error) => void;
41+
}
42+
43+
type _WatchQueryOptions = Omit<WatchQueryOptions, 'query'>; // exclude query prop because it causes type incorrectly error
44+
export interface VueApolloQueryOptions<V, R> extends _WatchQueryOptions {
45+
query: ((this: ApolloVueThisType<V>) => DocumentNode) | DocumentNode;
46+
variables?: VariableFn<V>;
47+
update?: (this: ApolloVueThisType<V>, data: R) => any;
48+
result?: (this: ApolloVueThisType<V>, data: R, loader: any, netWorkStatus: NetworkStatus) => void;
49+
error?: (this: ApolloVueThisType<V>, error: any) => void;
50+
loadingKey?: string;
51+
watchLoading?: (isLoading: boolean, countModifier: number) => void;
52+
skip?: (this: ApolloVueThisType<V>) => boolean | boolean;
53+
manual?: boolean;
54+
subscribeToMore?: ApolloVueSubscribeToMoreOptions<V> | ApolloVueSubscribeToMoreOptions<V>[];
55+
}
56+
57+
export interface VueApolloMutationOptions<V, R> extends MutationOptions<R> {
58+
mutation: DocumentNode;
59+
variables?: VariableFn<V>;
60+
optimisticResponse?: ((this: ApolloVueThisType<V>) => any) | Object;
61+
}
62+
63+
export interface VueApolloSubscriptionOptions<V, R> extends SubscriptionOptions {
64+
query: DocumentNode;
65+
variables?: VariableFn<V>;
66+
result?: (this: V, data: R) => void;
67+
}
68+
69+
type Query<V> = (key: string, options: VueApolloQueryOptions<V, any>) => void;
70+
type Mutate<V, R=any> = <R=any>(params: VueApolloMutationOptions<V, R>) => Promise<R>;
71+
type Subscribe<R=any> = <R=any>(params: SubscriptionOptions) => ObservableQuery<R>;
72+
export interface ApolloProperty<V> {
73+
[key: string]: Query<V> | Mutate<V> | Subscribe; // smart query
74+
queries: any;
75+
mutate: Mutate<V>;
76+
subscribe: Subscribe;
77+
}
78+
type QueryComponentProperty<V> = ((this: ApolloVueThisType<V>) => VueApolloQueryOptions<V, any>) | VueApolloQueryOptions<V, any>
79+
type SubscribeComponentProperty<V> = VueApolloSubscriptionOptions<V, any> | { [key: string]: VueApolloSubscriptionOptions<V, any> }
80+
81+
export interface VueApolloComponentOption<V> extends VueApolloOptions {
82+
[key: string]: QueryComponentProperty<V> | SubscribeComponentProperty<V> | string | boolean | Function | undefined;
83+
$subscribe?: SubscribeComponentProperty<V>;
84+
}

0 commit comments

Comments
 (0)