LoginSignup
12
8

More than 3 years have passed since last update.

Vue.js で JSDoc により実装をせずに data などの型指定をする

Last updated at Posted at 2019-08-13

Vue.js で JSDoc により実装をせずに data などの型指定をする

Vue.js で dataprops の中身を書かず、 JSDoc により型定義を行います。
Type Vue without TypeScript を参考にしています。

実例

jsdoc-js.vue
<script>
// // @ts-check
import Vue from 'vue'

/**
 * @typedef {Object} Props
 * @property {string|number} propId ぷろっぷあいでい
 */
/**
 * @typedef {Object} Data
 * @property {number} id あいでぃい
 * @property {string} name なまえ
 */
/**
 * @typedef {Object} Computed
 * @property {string} computedId けいさんされたあいでい
 */
/**
 * @typedef {Object} Methods
 * @property {{(name): boolean}} checkName なまえをちぇっくするめそっど
 */

export default Vue.extend({
  /** @type {import("vue/types/options").RecordPropsDefinition<Props>} */
  props: {
    propId: {
      type: String,
      default: () => 'aidei',
    },
  },
  /** @typedef {import("vue/types/options").DataDef<Data, Props, Vue>} DataDef */
  /**
   * @type {DataDef}
   * @return {Data}
   */
  data() {
    return {}
  },
  /** @type {import("vue/types/options").Accessors<Computed>} */
  computed: {},
  watch: {
    // WatchOptionsWithHandler だと handler の型指定がうまくいかず、
    // WatchOptionsWithHandler を指定しなくとも deep などはサジェストされるのでこの指定は不要かも
    //* * @type {import('vue/types/options').WatchOptionsWithHandler<Data['id']>} */
    id: {
      // WatchOptionsWithHandler だとなぜか val が any になってしまう・・・
      /** @type {import('vue/types/options').WatchHandler<Data['id']>} */
      handler(val, oldVal) {
        console.log(val, oldVal)
      },
    },
  },
  mounted() {
    this.propId
  },
  /** @type {Methods} */
  methods: {},
})
</script>

この例では props.propId を除いて data.id, data.name, computed.computedId, methods.checkName がそれぞれ「実装された」ことになっています。
※厳密には実装されておらず、使用しようとすると実行時にはエラーが起きます。

そこで VSCode などで見てみると・・・

props
props.jpg

data
data.jpg

computed
computed.jpg

methods
methods.jpg

それぞれ型、コメントなどが反映されていることがわかります。
後は実装するだけです。

いちいち JSDoc の内容と照らし合わせるのがめんどくさい

JSDoc の記述の通り実装するといっても実装の有無をいちいち確認するのは面倒です。
そこで・・・

ts-check-activate
<script>
// @ts-check

最初の行の //// @ts-check としていた記述を // @ts-check として TypeScript のチェックを有効化 しましょう。

problems.jpg

エラーがドバーっと出ました。

Type '(this: Readonly<Props> & Vue) => {}' is not assignable to type 'DataDef<Data, Props, Vue>'.
  Type '(this: Readonly<Props> & Vue) => {}' is not assignable to type '(this: Readonly<Props> & Vue) => Data'.

Type '(this: Readonly<Props> & Vue) => {}' is not assignable to type 'DataDef<Data, Props, Vue>'.
  Type '(this: Readonly<Props> & Vue) => {}' is not assignable to type '(this: Readonly<Props> & Vue) => Data'.
    Type '{}' is missing the following properties from type 'Data': id, name

Property 'computedId' is missing in type '{}' but required in type 'Accessors<Computed>'.

Property 'checkName' is missing in type '{}' but required in type 'Methods'.

それぞれのエラーを潰していく形で実装すれば自然と必要な実装がそろうでしょう・・・。

実例 (export default {}) の場合

jsdoc-export-object-literal.vue
<script>
// // @ts-check
/** @typedef {import("vue").default} Vue */

export default {
  /**
   * @typedef {Object} Props
   * @property {string|number} propId ぷろっぷあいでい
   */
  /** @type {import("vue/types/options").RecordPropsDefinition<Props>} */
  props: {
    propId: {
      type: String,
      default: () => 'aidei',
    },
  },
  /**
   * @typedef {Object} Data
   * @property {number} id あいでぃい
   * @property {string} name なまえ
   */
  /**
   * @typedef {import("vue/types/options").DataDef<Data, Props, Vue>} DataDef
   */
  /**
   * @type {DataDef}
   * @return {Data}
   */
  data() {
    return {};
  },
  /**
   * @typedef {Object} Computed
   */
  /**
   * @type {import("vue/types/options").Accessors<Computed>}
   */
  computed: {},
  watch: {
    id: {
      /** @type {import('vue/types/options').WatchHandler<Data['id']>} */
      handler(val, oldVal) {
        console.log(val, oldVal)
      },
    },
  },
  mounted() {
    this.id
  },
  /**
   * @typedef {Object} Methods
   * @property {{(name): boolean}} checkName なまえをちぇっくするめそっど
   */
  /** @type {Methods} */
  methods: {},
};
</script>

JSDoc の記述をまとめる

とりあえずできたのは Vue.extend のほうです。

Vue.extend 使用

jsdoc-extend-no-import
<script>
// // @ts-check
import Vue from 'vue';
/**
 * @typedef {Object} Props
 * @property {string|number} propId ぷろっぷあいでい
 */
/**
 * @typedef {Object} Data
 * @property {number} id あいでぃい
 * @property {string} name なまえ
 */
/**
 * @typedef {Object} Computed
 * @property {string} computedId けいさんされたあいでい
 */
/**
 * @typedef {Object} Methods
 * @property {{(name): boolean}} checkName なまえをちぇっくするめそっど
 */

// eslint-disable-next-line
/** @typedef {import("vue/types/options").ThisTypedComponentOptionsWithRecordProps<Vue, Data, Methods, Computed, Props>} Options */
/** @type {Options} */
export const options = {
  props: {},
  data() {
    return {};
  },
  computed: {},
  methods: {},
};

export default Vue.extend(options);
</script>

Vue.extend よけいな import

エラーがでるかもです

 ERROR  Failed to compile with 1 errors

This dependency was not found:

* vue/types/options in ./node_modules/babel-loader/lib??ref--2-0!./node_modules/vue-loader/lib??vue-loader-options!./pages/sample/jsdoc.vue?vue&type=script&lang=js&

To install it, you can run: npm install --save vue/types/options
jsdoc-extend
<script>
// // @ts-check
import Vue from 'vue';
import {
  // eslint-disable-next-line
  ThisTypedComponentOptionsWithRecordProps as Options
} from 'vue/types/options';

/**
 * @typedef {Object} Props
 * @property {string|number} propId ぷろっぷあいでい
 */
/**
 * @typedef {Object} Data
 * @property {number} id あいでぃい
 * @property {string} name なまえ
 */
/**
 * @typedef {Object} Computed
 * @property {string} computedId けいさんされたあいでい
 */
/**
 * @typedef {Object} Methods
 * @property {{(name): boolean}} checkName なまえをちぇっくするめそっど
 */

/** @type {Options<Vue, Data, Methods, Computed, Props>} */
export const options = {
  props: {},
  data() {
    return {};
  },
  computed: {},
  methods: {},
};

export default Vue.extend(options);
</script>
12
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
8