Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
4
Help us understand the problem. What is going on with this article?
@ysKuga

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

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>
4
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
4
Help us understand the problem. What is going on with this article?