- 
      
- 
        Save pondpaun7z/6e7e563fa1d371701a10a261da10cad5 to your computer and use it in GitHub Desktop. 
Revisions
- 
        RISCfuture revised this gist Apr 8, 2020 . 1 changed file with 5 additions and 3 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -119,12 +119,14 @@ import {Prop} from 'vue-property-decorator' @Component export default class MyView extends Vue { @Prop({type: String, required: true}) username!: string } ``` Note that unfortunately you have to declare the type twice: Once in the prop attributes (for Vue's type-checking) and once for TypeScript. Also note that for built-ins, Vue requires the boxed class (`String`) whereas TypeScript requires the primitive (`string`). Also note that, as of TypeScript 2.7, you need to _definitely define_ the property (hence the bang after the property name). ### Getters Change Vue getters into TypeScript getters: @@ -349,7 +351,7 @@ import {Getter} from 'vuex-class' @Component export default class PostsIndex extends Vue { @Getter getPosts!: Post[] } ``` @@ -366,7 +368,7 @@ import {Action} from 'vuex-class' @Component export default class PostsIndex extends Vue { @Action loadPosts!: ({commit, state}: ActionContext<PostsState, RootState>, {force}: {force: boolean}) => Promise<boolean> } ``` 
- 
        RISCfuture revised this gist Oct 31, 2019 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -14,7 +14,7 @@ If you want to see a commit on a project accomplishing this migration, visit htt 4. The `test:` regex in this file is wrong too. If you want, you can tighten it up by changing it to `/\.(ts|tsx)(\.erb)?$/`. 5. Run `yarn add vue-class-component vue-property-decorator vuex-class @types/node`. 6. If you have any JS libraries that need type-safety, then Yarn-install their `@types` packages. For example, I ran `yarn add @types/lodash @types/moment @types/mapbox-gl`. (Most guides tell you to include these modules as `devDependencies`, but because Webpacker transpiles the code in the production environment, you will need to make them production dependencies.) 
- 
        RISCfuture revised this gist Jun 12, 2019 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,7 +2,7 @@ These instructions assume you already have a Rails 5.2 project using Webpacker 4 with Vue 2 and Vuex 3. I'll show you how to add TypeScript to the project, and type-safe your Vue components, including single-file components (SFCs). This document will not teach you TypeScript syntax or type theory. It also assumes your code already works without TypeScript. You shouldn't use this article to, for example, get started with Vuex, because I'm leaving out lots of necessary boilerplate code and focusing just on TypeScript changes. If you want to see a commit on a project accomplishing this migration, visit https://github.com/RISCfuture/AvFacts/commit/666a02e58b4626a074a03812ccdd193a3891a954. ## Setup 
- 
        RISCfuture revised this gist Jun 12, 2019 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -14,9 +14,9 @@ If you want to see a commit on a project accomplishing this migration, visit htt 4. The `test:` regex in this file is wrong too. If you want, you can tighten it up by changing it to `/\.(ts|tsx)(\.erb)?$/`. 5. Run `yarn add vue-class-component vue-property-decorator vuex-class @node/types`. 6. If you have any JS libraries that need type-safety, then Yarn-install their `@types` packages. For example, I ran `yarn add @types/lodash @types/moment @types/mapbox-gl`. (Most guides tell you to include these modules as `devDependencies`, but because Webpacker transpiles the code in the production environment, you will need to make them production dependencies.) ## JavaScript Changes 
- 
        RISCfuture revised this gist Jun 11, 2019 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,7 +2,7 @@ These instructions assume you already have a Rails 5.2 project using Webpacker 4 with Vue 2 and Vuex 3. I'll show you how to add TypeScript to the project, and type-safe your Vue components, including single-file components (SFCs). This document will not teach you TypeScript syntax or type theory. It also assumes your code already works without TypeScript. You shouldn't use this article to, for example, get started with Vuex, because I'm leaving out lots of necessary boilerplate code and focusing just on TypeScript changes. If you want to see a commit on a project accomplishing this migration, visit https://github.com/RISCfuture/AvFacts/commit/66344fed8477b6fb7b03f0a477e2eba58e271e04. ## Setup @@ -14,7 +14,7 @@ If you want to see a commit on a project accomplishing this migration, visit htt 4. The `test:` regex in this file is wrong too. If you want, you can tighten it up by changing it to `/\.(ts|tsx)(\.erb)?$/`. 5. Run `yarn add vue-class-component vue-property-decorator vuex-class`. 6. If you have any JS libraries that need type-safety, then Yarn-install their `@types` packages. For example, I ran `yarn add --dev @types/lodash @types/moment @types/mapbox-gl`. 
- 
        RISCfuture revised this gist Jun 11, 2019 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -230,7 +230,7 @@ export default class MyView extends Vue { } ``` If you use `$el`, `$parent`, or `$children`, you can type-declare those as well: ``` ts import Vue from 'vue' 
- 
        RISCfuture revised this gist Jun 11, 2019 . 1 changed file with 3 additions and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -98,14 +98,16 @@ import Component from 'vue-class-component' @Component export default class MyView extends Vue { counter: number = 0 userName?: string = null mounted() { this.userName = getUsername() } } ``` **Important note:** Your instance properties _must_ be initialized with a default value to convert them to reactive data. Make them optional and initialize them to `null` if necessary. ### Props Use the `@Prop` decorator to declare your properties. Pass the prop attributes as decorator options: 
- 
        RISCfuture revised this gist Jun 11, 2019 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -333,7 +333,7 @@ Finally, refine the type of each module that you create: ``` ts const posts: Module<PostsState, RootState> = {state, getters, mutations, actions} export default posts ``` ### Mapped Getters 
- 
        RISCfuture revised this gist Jun 9, 2019 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,7 +2,7 @@ These instructions assume you already have a Rails 5.2 project using Webpacker 4 with Vue 2 and Vuex 3. I'll show you how to add TypeScript to the project, and type-safe your Vue components, including single-file components (SFCs). This document will not teach you TypeScript syntax or type theory. It also assumes your code already works without TypeScript. You shouldn't use this article to, for example, get started with Vuex, because I'm leaving out lots of necessary boilerplate code and focusing just on TypeScript changes. If you want to see a commit on a project accomplishing this migration, visit https://github.com/RISCfuture/AvFacts/commit/a560b7da73ea277e04707b19c7736545a5be233a. ## Setup 
- 
        RISCfuture revised this gist Jun 9, 2019 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,6 +2,8 @@ These instructions assume you already have a Rails 5.2 project using Webpacker 4 with Vue 2 and Vuex 3. I'll show you how to add TypeScript to the project, and type-safe your Vue components, including single-file components (SFCs). This document will not teach you TypeScript syntax or type theory. It also assumes your code already works without TypeScript. You shouldn't use this article to, for example, get started with Vuex, because I'm leaving out lots of necessary boilerplate code and focusing just on TypeScript changes. If you want to see a commit on a project accomplishing this migration, visit https://github.com/RISCfuture/AvFacts/commit/93821c51e7b38b236c372ec20f032845af96f8d1. ## Setup 1. Run `rails webpacker:install:typescript`. This should modify `config/webpacker.yml` and `config/webpack/environment.js` (leave those changes), add `tsconfig.json` and `config/webpack/loaders/typescript.js` (leave those files), and add some other files in `app/javascript` (delete those files). 
- 
        RISCfuture revised this gist Jun 9, 2019 . 1 changed file with 7 additions and 9 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -381,7 +381,13 @@ declare module '*.jpg' { } ``` This tells TypeScript that JPEG files are imported as string URLs by Webpack. Add whatever other image extensions you need. You must add the following to your `tsconfig.json` file: ``` "files": [ "app/frontend/shims.d.ts" ] ``` Then, where you import those images, handle them like they're strings: @@ -418,12 +424,4 @@ declare module 'config/secrets.js' { const secrets: Secrets export default secrets } ``` 
- 
        RISCfuture revised this gist Jun 9, 2019 . 1 changed file with 29 additions and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -368,6 +368,34 @@ export default class PostsIndex extends Vue { Unfortunately, as with Getters, you need to specify the signature each time you use the Action decorator, even though you specified it in the Vuex code. ### Importing images If you are using `import` to bring any images into your JavaScript or Vue components, you will have to modify your code to inform TypeScript how to import those images. You can add a shim file, say `app/javascript/shims.d.ts` that looks like this: ``` ts declare module '*.jpg' { const URL: string export default URL } ``` This tells TypeScript that JPEG files are imported as string URLs by Webpack. Add whatever other image extensions you need. Then, where you import those images, handle them like they're strings: ``` ts import Logo from 'images/logo.jpg' @Component export default class Banner extends Vue { logoURL = Logo } ``` In your `render()` method or SFC view, you can then use `this.logoURL` to access the URL for the image. ## ERb Files Unfortunately, the `.ts.erb` file syntax doesn't seem to be recognized correctly, even after fixing the regular expression in `config/webpack/loaders/typescript.js`. The only way I've figured out how to do it is to leave it as a `.js.erb` file, and add a TypeScript shim file. @@ -380,7 +408,7 @@ export default { } ``` You can add a shim file (see the previous section on importing images) that looks like this: ``` ts declare module 'config/secrets.js' { 
- 
        RISCfuture revised this gist Jun 9, 2019 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -312,10 +312,10 @@ const mutations = { And add types to your root and module actions as appropriate: ``` ts import {ActionContext} from 'vuex' const actions = { loadPosts({commit, state}: ActionContext<PostsState, RootState>, {force}: {force: boolean}): Promise<boolean> { if (state.postsLoaded && !force) return Promise.resolve(false) return new Promise((resolve, reject) => { commit('startLoading') 
- 
        RISCfuture revised this gist Jun 9, 2019 . 1 changed file with 14 additions and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -210,7 +210,7 @@ export default class MyView extends Vue { } ``` ### Refs, `$parent`, `$children`, etc. To add type-safety to your refs, explicitly declare the type of the `$refs` property: @@ -226,6 +226,19 @@ export default class MyView extends Vue { } ``` If you use `$parent`, or `$children`, you can type-declare those as well: ``` ts import Vue from 'vue' import Component from 'vue-class-component' @Component export default class MyView extends Vue { $parent!: MyParentView $children!: MySubview[] } ``` ## Vue Mixins If you're using Vue options objects as mixins, you'll want to change those to decorated classes as well. Write them just like you would write a Vue class as described in the previous section. 
- 
        RISCfuture revised this gist Jun 9, 2019 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -40,6 +40,8 @@ If you're using Vue-Router, change your `routes.js` to `routes.ts` and type your I'm using vue-class-component to rewrite my Vue components as classes using decorators. I'll show you the syntax changes here. These changes work for both SFCs and traditional Vue components using `Vue.extend`. You will first need to change the `<script>` tag to `<script lang="ts">`. ### Class definition Change your Vue options object into a TypeScript class. Make your class definition to look like this: 
- 
        RISCfuture revised this gist Jun 9, 2019 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -12,9 +12,9 @@ These instructions assume you already have a Rails 5.2 project using Webpacker 4 4. The `test:` regex in this file is wrong too. If you want, you can tighten it up by changing it to `/\.(ts|tsx)(\.erb)?$/`. 5. Run `yarn add --dev vue-class-component vue-property-decorator vuex-class`. 6. If you have any JS libraries that need type-safety, then Yarn-install their `@types` packages. For example, I ran `yarn add --dev @types/lodash @types/moment @types/mapbox-gl`. ## JavaScript Changes 
- 
        RISCfuture revised this gist Jun 9, 2019 . 1 changed file with 37 additions and 3 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -10,9 +10,11 @@ These instructions assume you already have a Rails 5.2 project using Webpacker 4 3. Edit the `config/webpack/loaders/typescript.js` file. The options for the `ts-loader` loader should be `PnpWebpackPlugin.tsLoaderOptions()`. Change this to `PnpWebpackPlugin.tsLoaderOptions({appendTsSuffixTo: [/\.vue$/]})`. This will ensure that TypeScript recognizes `.vue` files as processable. 4. The `test:` regex in this file is wrong too. If you want, you can tighten it up by changing it to `/\.(ts|tsx)(\.erb)?$/`. 5. Run `yarn install --dev vue-class-component vue-property-decorator vuex-class`. 6. If you have any JS libraries that need type-safety, then Yarn-install their `@types` packages. For example, I ran `yarn install --dev @types/lodash @types/moment @types/mapbox-gl`. ## JavaScript Changes @@ -349,4 +351,36 @@ export default class PostsIndex extends Vue { } ``` Unfortunately, as with Getters, you need to specify the signature each time you use the Action decorator, even though you specified it in the Vuex code. ## ERb Files Unfortunately, the `.ts.erb` file syntax doesn't seem to be recognized correctly, even after fixing the regular expression in `config/webpack/loaders/typescript.js`. The only way I've figured out how to do it is to leave it as a `.js.erb` file, and add a TypeScript shim file. Assume you have a `app/javascript/config/secrets.js.erb` file like so: ``` js-erb export default { mapboxAccessToken: '<%= Rails.application.credentials.mapbox_access_token %>' } ``` You can add a shim file, say `app/javascript/shims.d.ts` that looks like this: ``` ts declare module 'config/secrets.js' { interface Secrets { mapboxAccessToken: string } const secrets: Secrets export default secrets } ``` Then, add the following to your `tsconfig.json` file: ``` "files": [ "app/frontend/shims.d.ts" ] ``` 
- 
        RISCfuture revised this gist Jun 8, 2019 . 1 changed file with 7 additions and 0 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -310,6 +310,13 @@ const actions = { } ``` Finally, refine the type of each module that you create: ``` ts const posts: Module<PostsState, RootState> = {state, getters, mutations, actions} export default settings ``` ### Mapped Getters Use the Getter decorator to map Vuex getters into your Vue components. 
- 
        RISCfuture revised this gist Jun 8, 2019 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -36,7 +36,7 @@ If you're using Vue-Router, change your `routes.js` to `routes.ts` and type your ## Vue Components I'm using vue-class-component to rewrite my Vue components as classes using decorators. I'll show you the syntax changes here. These changes work for both SFCs and traditional Vue components using `Vue.extend`. ### Class definition @@ -338,7 +338,7 @@ import {Action} from 'vuex-class' @Component export default class PostsIndex extends Vue { @Action loadPosts: ({commit, state}: ActionContext<PostsState, RootState>, {force}: {force: boolean}) => Promise<boolean> } ``` 
- 
        RISCfuture created this gist Jun 8, 2019 .There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,345 @@ # Adding TypeScript to a Rails + Webpacker + Vue project These instructions assume you already have a Rails 5.2 project using Webpacker 4 with Vue 2 and Vuex 3. I'll show you how to add TypeScript to the project, and type-safe your Vue components, including single-file components (SFCs). This document will not teach you TypeScript syntax or type theory. It also assumes your code already works without TypeScript. You shouldn't use this article to, for example, get started with Vuex, because I'm leaving out lots of necessary boilerplate code and focusing just on TypeScript changes. ## Setup 1. Run `rails webpacker:install:typescript`. This should modify `config/webpacker.yml` and `config/webpack/environment.js` (leave those changes), add `tsconfig.json` and `config/webpack/loaders/typescript.js` (leave those files), and add some other files in `app/javascript` (delete those files). 2. Edit your `tsconfig.json` file. If it's not already, change the `module` setting to `"es6"` (this will allow you to use ES6-style `import`/`export` syntax). Add `"strict": true` (because you want strict type-safety checking, right?). 3. Edit the `config/webpack/loaders/typescript.js` file. The options for the `ts-loader` loader should be `PnpWebpackPlugin.tsLoaderOptions()`. Change this to `PnpWebpackPlugin.tsLoaderOptions({appendTsSuffixTo: [/\.vue$/]})`. This will ensure that TypeScript recognizes `.vue` files as processable. 4. Run `yarn install --dev vue-class-component vue-property-decorator vuex-class`. 5. If you have any JS libraries that need type-safety, then Yarn-install their `@types` packages. For example, I ran `yarn install --dev @types/lodash @types/moment @types/mapbox-gl`. ## JavaScript Changes Now start adding types to your JavaScript files! The easiest is just your pure `.js` files (not Vue components). Simply change the extension to `.ts` and begin adding TypeScript syntax. See the [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/basic-types.html) to learn more. If you are importing any Yarn modules for which you added `@types` packages, you will need to change the import syntax from, e.g.: ``` js import _ from 'lodash` ``` to ``` js import * as _ from 'lodash' ``` You'll probably want to add some kind of `types.ts` in your `app/javascript` where you can export out all the new application types you'll be adding. Some people like to modify their `tsconfig.json` so this types file is added to the global namespace, but I prefer leaving the global namespace unadulterated and importing the types I need into each `.ts` file. If you're using Vue-Router, change your `routes.js` to `routes.ts` and type your routes object as `RouteConfig[]`. ## Vue Components I'm using vue-class-component to rewrite my Vue components as classes using decorators. I'll show you the syntax changes here. These changes work for both SCFs and traditional Vue components using `Vue.extend`. ### Class definition Change your Vue options object into a TypeScript class. Make your class definition to look like this: ``` ts import Vue from 'vue' import Component from 'vue-class-component' @Component export default class MyView extends Vue { } ``` ### Components If your view includes components, add them like so: ``` ts import Vue from 'vue' import MyComponent from './MyComponent.vue' @Component {( components: [MyComponent] )} export default class MyView extends Vue { } ``` ### Mixins Use the `mixins` method to add mixins to your class: ``` ts import Component, {mixins} from 'vue-class-component' import MyMixin from 'MyMixin.ts' @Component export default class MyView extends mixins(MyMixin) { } ``` The `mixins` method can any number of mixins. ### Data Change your data into instance properties. If you need to dynamically initialize any data, do it in the `mounted()` or `created()` hook instead of in the `data()` method. ``` ts import Vue from 'vue' import Component from 'vue-class-component' @Component export default class MyView extends Vue { counter: number = 0 userName: string mounted() { this.userName = getUsername() } } ``` ### Props Use the `@Prop` decorator to declare your properties. Pass the prop attributes as decorator options: ``` ts import Vue from 'vue' import Component from 'vue-class-component' import {Prop} from 'vue-property-decorator' @Component export default class MyView extends Vue { @Prop({type: String, required: true}) username: string } ``` Note that unfortunately you have to declare the type twice: Once in the prop attributes (for Vue's type-checking) and once for TypeScript. Also note that for built-ins, Vue requires the boxed class (`String`) whereas TypeScript requires the primitive (`string`). ### Getters Change Vue getters into TypeScript getters: ``` ts import Vue from 'vue' import Component from 'vue-class-component' @Component export default class MyView extends Vue { get fullName(): string { return this.firstName + " " + this.lastName } } ``` Feel free to mark these getters as `private` or `protected` if appropriate. ### Methods Make your methods normal instance methods. ``` ts import Vue from 'vue' import Component from 'vue-class-component' @Component export default class MyView extends Vue { setFilter(filter: string) { this.$refs.filter.value = filter } } ``` Also feel free to mark these `private` or `protected` if appropriate. ### Filters Filters are not expressible in the vue-class-component syntactic sugar, and so must be passed as raw options to the Component decorator: ``` ts import Vue from 'vue' import Component from 'vue-class-component' @Component({ filters: { formatDate(date: Date) { return moment(date).format('MMM D YYYY') } } }) export default class MyView extends Vue { } ``` You can still add types to the filter methods though. ### Hooks Hooks also become normal instance methods. ``` ts import Vue from 'vue' import Component from 'vue-class-component' @Component export default class MyView extends Vue { mounted() { this.loadData() } } ``` ### Watchers Watchers become normal instance methods, annotated with the Watch component. They can be named whatever you'd like, and you can have multiple methods act as responders to a single watcher. ``` ts import Vue from 'vue' import Component from 'vue-class-component' import {Watch} from 'vue-property-decorator' @Component export default class MyView extends Vue { @Watch('currentSelection') onCurrentSelectionChanged(before: string, after: string) { this.loadDataForSelection(after) } } ``` ### Refs To add type-safety to your refs, explicitly declare the type of the `$refs` property: ``` ts import Vue from 'vue' import Component from 'vue-class-component' @Component export default class MyView extends Vue { $refs!: { filterForm: HTMLFormElement } } ``` ## Vue Mixins If you're using Vue options objects as mixins, you'll want to change those to decorated classes as well. Write them just like you would write a Vue class as described in the previous section. ``` ts import Vue from 'vue' import Component from 'vue-class-component' @Component export default class AutoRefresh extends Vue { refreshTimer?: number refreshHandler: () => void mounted() { this.refreshTimer = window.setInterval(this.refreshInterval, 5000) } } ``` ## Vuex Modules In your root Vuex file (for example, `app/javascript/store/index.ts`), define an interface with all your root state properties: ``` ts export interface RootState { authenticated: boolean } ``` Change your initialization code to: ``` ts new Vuex.Store<RootState>(options) ``` For each module, define an interface with the properties for that module's state: ``` ts export interface PostsState { posts: Post[] postsLoaded: boolean postsError?: Error } ``` Add types to your root and module getters as appropriate: ``` ts const getters = { getPosts(state: PostsState) { return state.posts } } ``` Add types to your root and module mutations as appopriate: ``` ts interface AppendPostsPayload { posts: Post[] page: number } const mutations = { appendPosts(state: PostsState, {posts, page}: AppendPostsPayload) { state.posts = state.posts.concat(posts) } } ``` And add types to your root and module actions as appropriate: ``` ts import {Commit} from 'vuex' const actions = { loadPosts({commit, state}: {commit: Commit, state: PostsState}, {force}: {force: boolean}): Promise<boolean> { if (state.postsLoaded && !force) return Promise.resolve(false) return new Promise((resolve, reject) => { commit('startLoading') // [...] commit('finishLoading') resolve(true) }) } } ``` ### Mapped Getters Use the Getter decorator to map Vuex getters into your Vue components. ``` ts import Vue from 'vue' import Component from 'vue-class-component' import {Getter} from 'vuex-class' @Component export default class PostsIndex extends Vue { @Getter getPosts: Post[] } ``` Unfortunately, for type-safety, you need to specify the type each time you use the Getter decorator, even though you specified it in the Vuex code. ### Mapped Actions Use the Action decorator to map Vuex actions into your Vue components. ``` ts import Vue from 'vue' import Component from 'vue-class-component' import {Action} from 'vuex-class' @Component export default class PostsIndex extends Vue { @Action loadPosts: ({commit, state}: {commit: Commit, state: PostsState}, {force}: {force: boolean}) => Promise<boolean> } ``` Unfortunately, as with Getters, you need to specify the signature each time you use the Action decorator, even though you specified it in the Vuex code.