Vue.js で JSDoc により実装をせずに data などの型指定をする
Vue.js で data
や props
の中身を書かず、 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 などで見てみると・・・
それぞれ型、コメントなどが反映されていることがわかります。
後は実装するだけです。
いちいち JSDoc の内容と照らし合わせるのがめんどくさい
JSDoc の記述の通り実装するといっても実装の有無をいちいち確認するのは面倒です。
そこで・・・
ts-check-activate
<script>
// @ts-check
最初の行の //// @ts-check
としていた記述を // @ts-check
として TypeScript のチェックを有効化 しましょう。
エラーがドバーっと出ました。
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>