Reputation: 6104
I'm trying to add Flow to the Vue 2 webpack-template. For the record, I'm on runtime-only (files follow the .vue
format / standard).
My first attempt was to use flow through the cli, which I realized it's not going to work because it didn't know how to handle .vue
files.
My second attempt was to add a webpack loader (namely flow-status-webpack-plugin) and run Flow check as part of the build (like eslint
works for example). That didn't work out, so I looked into other options.
My third attempt was to use a babel plugin, which was fairly successful at first. I used babel-plugin-typecheck + babel-plugin-syntax-flow. There's no output in Webpack, however a type error would break the app. I'm fine with this approach; it'll work fine with a CI and break the build.
Here's how my .babelrc
looked:
{
...
"plugins": [
...
["typecheck", {
"disable": {
"production": true
}
}],
"syntax-flow",
"transform-flow-strip-types"
],
...
}
At this point, Flow works as expected for global methods, but doesn't work inside a Vue component:
<template>...</template>
<script>
/* @flow */
const flowIt = (a: number): number => {
return a * 10
}
flowIt(20)
flowIt('bah') // Uncaught TypeError: Value of argument "a" violates contract. Expected: number Got: string
export default {
mounted: function () {
flowIt(20)
flowIt('bah') // Sees nothing wrong here
}
}
</script>
<style>...</style>
On top of that, the goal is to not change the app code because of Flow. Ideally, I'd just use Vue as normally:
<template>...</template>
<script>
/* @flow */
export default {
methods: {
flowIt (a: number): number {
return a * 10
}
},
mounted: function () {
this.flowIt(20)
this.flowIt('bah') // Should throw a type error.
}
}
</script>
<style>...</style>
Not sure if this has that much to do with Vue as it has with my experience with Flow (hint: not that experienced). I'm thinking I need some type files that make Flow 'understand' how a Vue component is structured (same for directives I guess).
To those that have more experience with it, how did you get Flow to properly work with Vue + webpack?
Upvotes: 18
Views: 8281
Reputation: 18080
I have implemented a project template for vue
with flow
. https://github.com/wemake-services/wemake-vue-template It supports single file components, linting, tests with jest
, building, and server side rendering.
Here's what your components would look like:
<template>
...
</template>
<script>
// @flow
import Vue from 'vue'
import { Store } from 'vuex'
import Component from 'nuxt-class-component'
import { Getter, State } from 'vuex-class'
import AppLogo from '~/components/AppLogo'
import Comment from '~/components/Comment'
import type { CommentType, StateType } from '~/types'
@Component({
components: {
AppLogo,
Comment
}
})
export default class Index extends Vue {
@State('comments') comments: Array<CommentType>
@Getter('hasComments') hasComments: boolean
fetch (
{ store, app }: { store: Store<StateType>, app: Vue }
): Promise<Array<CommentType>> {
// Uncomment the next line to test flow types:
// console.log(this.comments + 12)
return store.dispatch('fetchComments', app)
}
}
</script>
It requires several things to configure:
flow
in its name from here: https://github.com/wemake-services/wemake-vue-template/blob/master/template/package.json.flowconfig
. This might be tricky: https://github.com/wemake-services/wemake-vue-template/blob/master/template/.flowconfig.babelrc
: https://github.com/wemake-services/wemake-vue-template/blob/master/template/.babelrceslint
: https://github.com/wemake-services/wemake-vue-template/blob/master/template/.eslintrcflow-typed install
after each normal installation: https://github.com/wemake-services/wemake-vue-template/blob/master/template/package.json#L12You are also able to annotate vuex
store: state, commit handlers, getters, and actions. You can use @vue-flow-typed/vuex
for this part.
That's how it looks:
type StateType = {
comments: string[]
}
function state (): StateType {
return {
comments: null
}
}
const getters = {
hasComments (state: StateType): boolean {
return Boolean(state.comments && state.comments.length > 0)
}
}
const mutations = {
'SET_COMMENTS': (
state: StateType, comments: string[]
) => {
state.comments = comments
}
}
const actions = {
async fetchComments (
{ commit, state }: ActionContext<StateType>
) {
const data = await Promise.resolve(['good', 'nice'])
commit('SET_COMMENTS', data)
// Uncomment next line to see typing in action:
// console.log(state.comments, state.fake)
return data
}
}
But beware, that it is still impossible to annotate some parts. Read more about known issues here: https://github.com/sobolevn/vue-flow-typed#known-problems
Upvotes: 1
Reputation: 6104
This is yet another approach to integrating flow & vue.
Meanwhile, flow
came to eslint
. Therefore, we can get flow errors straight as lint errors. It's a cleaner approach, but then flow becomes coupled with your build process (you can't run flow check
independently, but need to run your entire build pipeline via webpack to get the errors). Still waiting for this issue to be resolved to have full flow support in .vue
files as of 10th of May 2017.
For most cases that's fine, but some might still want the flexibility (and speed) of running flow check
. That might also depend on your CI setup.
Here's how you can setup flow and eslint:
Install deps
yarn add \
babel-plugin-syntax-flow \
babel-plugin-transform-class-properties \
babel-plugin-transform-flow-strip-types \
eslint \
babel-eslint \
eslint-plugin-html \
eslint-plugin-flowtype-errors \
eslint-plugin-vue \
eslint-config-vue \
flow-bin \
-D
Configure .babelrc
{
...
"plugins": [
"babel-plugin-transform-class-properties",
"babel-plugin-syntax-flow",
"babel-plugin-transform-flow-strip-types"
]
}
Configure .eslintrc
{
"parser": "babel-eslint",
"plugins": [
"html",
"flowtype-errors"
],
"extends": [
"vue"
],
"rules": {
"flowtype-errors/show-errors": 2
}
}
Create a .flowconfig
file. It can be empty if you have nothing to configure.
No other workarounds required in this case, you can then just use /* @flow */
in the script tags in any of your .vue
files.
See the original post here.
Upvotes: 6
Reputation: 13393
I think this has been solved in the meantime and now you can use Flow with Vue-components without hacks. See this rather brilliant article for config details: https://alligator.io/vuejs/components-flow/
Upvotes: 2
Reputation: 6104
In addition to Nik's answer, it's worth mentioning that combining his 'comment' strategy with a runtime checker makes 'the package' a tad more complete.
One way to do it is by using babel-plugin-tcomb. That'll make the runtime checker part of the webpack / build process (on save) + flow check
as part of a CI script.
For development, tcomb will do a runtime check and will throw an exception (console). It doesn't do static checks, so the following
<script>
/* @flow */
const flowIt = (a: number): number => {
return '' // Sees nothing wrong here, should be a number
}
// Vue component
export default {
...
}
</script>
won't work as expected. However, the following will:
<template>{{ foo('bar') }} <!-- Type error --></template>
<script>
/* @flow */
const flowIt = (a: number): number => {
return '' // Type error
}
// Vue component
export default {
methods: {
foo: (x) => { flowIt(x) // Type error }
},
mounted: () => {
flowIt([]) // Type error
}
}
</script>
That's not ideal, but it does check after each save and that'll catch most type errors. Worth mentioning: tcomb uses the same annotations (uses Flow internally), so it works out of the box.
Ofc, that's not good enough & kinda defeats the point of Flow. The solution to that is to still run flow check
on the CI, as mentioned. That requires a number of changes:
Update .flowconfig to load .vue files:
...
[options]
module.file_ext=.vue
module.file_ext=.js
...
Include the template & style block in the comment containing the @flow pragma; comment out the script tags (this approach was mentioned here):
/* @flow
<template>...</template>
<style>...</style>
*/
// <script>
...
// </script>
That's a bit awkward, but I could not find a better way. Ideally, Flow would be able to process <script>
tags in a HTML doc, but that's just on the wishlist for now (see issue).
Disable tcomb in production
{
...
"plugins": [
...
"syntax-flow",
"transform-flow-strip-types"
],
"env": {
"development": {
"plugins": ["tcomb"]
}
}
}
Upvotes: 4
Reputation: 1363
You can still use Flow for the JS portion of a .vue component, by commenting out the <template>
, <style>
and <script>
portions:
/* @flow
<style>
...style definitions here
</style>
<template>
...html...
</template>
*/
// <script>
export default {
methods: {
flowIt (a: number): number {
return a * 10
}
},
mounted: function () {
this.flowIt(20)
this.flowIt('bah') //Won't throw error, as flowIt is attached to
//this.
}
}
// </script>
The vue compiler will still recognize the <template>, <style> and <script>
sections even when commented, but the Flow type checker will ignore them and only process the proper javascript section.
Unfortunately, this won't get you 100% type coverage, as Flow will not be able to check functions and objects attached to this
(the Vue component itself), however, you can still benefit from Flow's type checking of calls out to external functions (e.g. Vuex actions and getters, other javascript imported modules), and if you have extended business logic within the component's methods, you can get some type safety when working with the method parameters.
Upvotes: 5