Vue.extend() 使用時の data 推論には computed などの定義が必要っぽい
Type Vue without TypeScript の JSDoc による戻り値などの型指定をやっていたところ $data
についてつまづいたので
Visual Studio Code
により Vue CLI 3 や Nuxt.js 2 などで試しました。
Vue.extend()
による $data
の推論に必要と思われる options
は・・・
computed
と methods
Vue Forum の投稿の
Vue2.5 でthisが推論されない の computed
のあたりを参考にしました。
<script>
import Vue from 'vue';
export default Vue.extend({
data() {
return {
name: 'taro',
};
},
// computed は中身が通常の関数、アロー関数どちらでも問題なし
// ただし定義をしない、あるいは空にすると $data 配下が any 扱いに
computed: {
computedName() {
return this.name
},
},
mounted() {
// (property) name: string
this.name;
},
// methods は空でも定義があればよく、
// 中身の定義をする場合でも通常の関数、アロー関数どちらでもよい
methods: {
test: () => 1,
exec() {},
},
});
</script>
あまりよくない computed
投稿時に下記のような実例としたが Symbol()
により computed
を定義すると
data
配下の要素をそのまま返すような computed
で使用したプロパティ自体が any
となる
<script>
import Vue from 'vue';
export default Vue.extend({
・・・
computed: {
[Symbol()]() {
return 0;
},
// [Symbol()]: () => 0,
// 以下の事例においては any とはならず定義した型が推論の結果となる
// aaa: () => 0,
// [`aaa`]: () => 0,
computedName() {
return this.name
},
},
mounted() {
// computedName() を定義する前までは string として扱われていた
// any
this.name
},
・・・
});
</script>
props
で default
定義にアロー関数が指定されているもの
<script>
import Vue from 'vue';
export default Vue.extend({
props: {
puroppu: {
type: Boolean,
default: () => false,
// アロー関数でないと $data 配下が any になるだけ
// default() {
// return false;
// },
},
},
data() {
return {
name: 'saburo',
};
},
mounted() {
// (property) ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<string, any>>.name: string
this.name;
},
});
</script>
踏み込んで調べてはいないですが props
配下にて default
をアロー関数で指定したものが存在していると
コメントに記載したような (property) ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<string, any>>.name: string
という推論?が行われ
結果 string
が取得できるようです。
かなり長い表示がされるので前述の computed
methods
のほうを使いたいと感じましたが
どうも props
配下に 1 つでも default
にアロー関数を使用しているとこちらの方式が優先されるもようです。
computed まわり
十分なプロパティ指定をしないと data
配下をそのまま返すような computed
の型は推論されない
十分な指定は
したのほうをさんしょう
<script>
import Vue from 'vue';
export default Vue.extend({
・・・
computed: {
computedName() {
// メソッド内部では正しく推論されるが
// (property) name: string
return this.name
},
},
mounted() {
// 使用時に推論されない
// any
this.computedName
},
・・・
});
</script>
computed
を推論させるために
2 種類の方法が考えられる模様です
-
型が明確な記述の使用
-
Type Vue without TypeScript の事例の通り JSDoc による
@returns
などの指定-
@returns
での指定 - 処理中において
@type
により型の明示された変数を戻り値として指定
-
<script>
import Vue from 'vue';
export default Vue.extend({
・・・
computed: {
/**
* @returns {string}
*/
computedName() {
return this.name
},
typedName() {
/** @type {string} */
const name = this.name
return name
},
fullName() {
return `sato ${this.name}`
},
// String のメソッドを使用しても推論されない模様
trimmedName() {
// (method) String.trim(): string
return this.name.trim()
},
},
mounted() {
// (property) computedName: string
this.computedName
// (property) typedName: string
this.typedName
// (property) fullName: string
this.fullName
// (property) trimmedName: any
this.trimmedName
},
・・・
});
</script>
props の推論
props
に指定した要素を推論させる場合
data
, computed
それぞれ推論可能な状態でさらに props
への注釈が必要である模様
- 直接型を指定する
-
type
,default
などを指定した構造の場合注釈としてconstructor
などの指定
<script>
import Vue from 'vue';
class Entity {
id = 1
name = 'entetei'
constructor() {}
}
export default Vue.extend({
・・・
props: {
/*
* マウスオーバーにより表示される推論の結果での type を @type への指定とする
* (property) propNum: {
* type: NumberConstructor;
* default(): number;
* }
*/
propNum: {
type: Number,
default() {
return 1
},
},
// type に Number や String などの指定をした場合
/** @type {NumberConstructor} */
propId: {
type: Number,
default() {
return 1
},
},
// 直接 Number などを指定した場合
propNo: Number,
// 独自に定義した場合の注釈
/** @type {{ new (): Entity }} */
propEntity: {
type: Entity,
default() {
return new Entity()
},
},
},
mounted() {
// (property) propId: number
this.propId
// (property) propNo: number
this.propNo
// (property) propEntity: Entity
this.propEntity
},
・・・
});
</script>
とりあえず推論される記述
props, data, computed のセット
<script>
import Vue from 'vue'
class Entity {
id = 1
name = 'entetei'
constructor() {}
}
export default Vue.extend({
props: {
/** @type {NumberConstructor} */
propId: {
type: Number,
default() {
return 1
},
},
propNo: Number,
/** @type {{ new (): Entity }} */
propEntity: {
type: Entity,
default() {
return new Entity()
},
},
// props に関数を指定する場合 default の戻り値でなく、default自体が初期値となるもよう
/** @type {FunctionConstructor} */
propFunction: {
type: Function,
default() {
return 1;
},
},
},
data() {
return {
name: 'taro',
}
},
computed: {
entity: () => Entity,
/**
* @returns {string}
*/
computedName() {
return this.name
},
typedName() {
/** @type {string} */
const name = this.name
return name
},
fullName() {
return `sato ${this.name}`
},
trimmedName() {
return this.name.trim()
},
},
mounted() {
// (property) propId: number
this.propId
// (property) propNo: number
this.propNo
// (property) propEntity: Entity
this.propEntity
// (property) propFunction: Function
this.propFunction
// (property) name: string
this.name
// (property) computedName: string
this.computedName
// (property) typedName: string
this.typedName
// (property) fullName: string
this.fullName
// props も含めて定義されていると data の直接の推論がきくもよう
// (property) trimmedName: string
this.trimmedName
},
methods: {},
})
</script>
不要な実装の記述をしたくない場合の JSDoc 利用
JSDoc 使用であれば props
computed
, data
への JSDoc の記述を行い
methods
を用意してあげればよいようです。
props
computed
, data
に具体的な実装が入った場合には
記述を修正する必要が発生しますが。
<script>
import Vue from 'vue';
/**
* @typedef {Object<string, unknown>} UnknownObject
*/
export default Vue.extend({
/** @type {UnknownObject} */
props: {},
/** @type {{new(): UnknownObject}} */
data() {
return {};
},
/** @type {UnknownObject} */
computed: {},
mounted() {
// CombinedVueInstance<Vue, new () => {
// [x: string]: unknown;
// }, {}, {
// [x: string]: ...;
// }, Readonly<Record<string, any>>>
this;
},
methods: {},
});
</script>