ネット上のJavaScript関連の文書を読むとTypeScriptで記述したコードを使って説明している人が増えてきました。そのためTypeScriptの経験がない人にとってはTypeScriptで記述したコードを読むのにストレスを感じているかもしれません。Vue 3ではVue自体がTypeScriptを使って記述されていることもあり特別な設定を行うことなくTypeScriptを利用することができるので今後TypeScriptで記述された文書が増えてくると思います。本文書ではTypeScriptで記述されたコードも読めるようにVue3でのTypeScriptの記述方法について簡単な例を使って解説していいます。TypeScriptは型チェックを行うことが主な役割なので型に注目して読み進めてください。

エディターにはVisual Studio Codeを利用しExtentionsにはVeturをインストールしています。

また下記の文書でもTypeScriptに関する情報を公開しています。

環境構築

Vue3のインストール

vue-cliコマンドを利用していvue3のインストールを行います。インストール中にTypeScriptを選択することができるので選択することでTypeScriptを利用することができます。

プロジェクトの名前は任意の名前をつけてください。ここではvue_typescriptという名前をつけています。


 % vue create vue_typescript
Vue CLiがインストールされていない場合はインストールを行ってください。またバージョンが古い場合は最新バージョンにアップデートを行ってください。本文書では、Vue CLI v4.5.11を利用しています。

インストールを開始するとpresetの選択画面が表示されるので、Manually select featuresを選択してください。


Vue CLI v4.5.11
? Please pick a preset: 
  Default ([Vue 2] babel, eslint) 
  Default (Vue 3 Preview) ([Vue 3] babel, eslint) 
❯ Manually select features  

機能の選択画面が表示されるのここでTypeScriptを忘れずに選択してください。TypeScriptまで移動して、Spaceキーを押して選択してください。


? Check the features needed for your project: 
 ◉ Choose Vue version
 ◉ Babel
❯◉ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◯ Router
 ◯ Vuex
 ◯ CSS Pre-processors
 ◉ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

次にVueのバージョンの選択画面が表示されるので、3.x(Preview)を選択してください。


? Choose a version of Vue.js that you want to start the project with 
  2.x 
❯ 3.x (Preview) 

以下オプション選択を行っていきますがすべてデフォルトの設定を選択していきます。Enterボタンを押すとデフォルトが選択されます。

class-style component syntaxの選択を聞かれるので”N”(No)を選択してください。YesかNoを選択することによる違いについては後ほど”class-style componentの選択”で説明を行っています。


? Use class-style component syntax? (y/N)  

Babel alongside TypeScriptを使うか聞かれるので”Y”(Yes)を選択します。Noを選択した場合はインストール後にbabel.config.jsファイルがプロジェクトディレクトリに作成されません。


? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? (Y/n) 

デフォルトのままEnterボタンをクリックします。


? Pick a linter / formatter config: (Use arrow keys)
❯ ESLint with error prevention only 
  ESLint + Airbnb config 
  ESLint + Standard config 
  ESLint + Prettier 
  TSLint (deprecated) 

ここでもデフォルトのままEnterボタンをクリックします。


? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◉ Lint on save
 ◯ Lint and fix on commit

ここでもデフォルトのままEnterボタンをクリックします。

In package.jsonを選択した場合はeslintの.eslintrc.jsファイルの内容がpackage.jsonファイルの中に記述されます。

? Where do you prefer placing config for Babel, ESLint, etc.? 
❯  In dedicated config files 
   In package.json 

ここまでの設定は以下の通りです。最後もそのままEnterボタンをクリックするとインストールが開始されます。


Vue CLI v4.5.11
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, TS, Lin
ter
? Choose a version of Vue.js that you want to start the project with 3.x (Previe
w)
? Use class-style component syntax? No
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfi
lls, transpiling JSX)? Yes
? Pick a linter / formatter config: Basic
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In package.json
? Save this as a preset for future projects? (y/N) No

class-style componentの選択による違い

class-style component syntaxをYesにした場合は、下記のようにApp.vueファイルの中でvue-class-componentsをimportして利用しており、Appコンポーネントがclassベースコンポーネントとして作成されています。


<script lang="ts">
import { Options, Vue } from 'vue-class-component';
import HelloWorld from './components/HelloWorld.vue';

@Options({
  components: {
    HelloWorld,
  },
})
export default class App extends Vue {}
</script>

デフォルトのNoにした場合はdefineComponentをimportしています。export defaultの中身がdefineComponent関数でWrapされていることがわかります。TypeScriptを利用する場合はdefineComponent関数でのWrapが必須となります。


<script lang="ts">
import { defineComponent } from 'vue';
import HelloWorld from './components/HelloWorld.vue';

export default defineComponent({
  name: 'App',
  components: {
    HelloWorld
  }
});
</script>

さらに比較としてTypeScriptをインストールしていない場合も確認しておきましょう。App.vueファイルの中身は下記の通りです。HelloWorldのみimportを行い、scriptタグのlang=”ts”が設定されていないという違いがあります。


<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>

vueの起動

インストールが完了したらインストールディレクトリに移動して下記のコマンドを実行してください。


 % cd vue_typescript 
 % npm run serve
//略
  App running at:
  - Local:   http://localhost:8080/ 
  - Network: http://192.168.2.138:8080/

  Note that the development build is not optimized.
  To create a production build, run npm run build.

ブラウザでhttp://localhost:8080/にアクセスするとVue.js + TypeScriptの文字が表示されていたら TypeScriptのインストールは完了しています。

vue3 + TypeScript
vue3 + TypeScript

shims-vue.d.tsファイル

インストールディレクトリを見るとshims-vue.d.tsファイルというものがあります。shims-vue.d.tsファイルはIDEやエディターに拡張子.vueファイルが何のファイルかというのを理解させるために利用するファイルです。

Visual Studio Codeを利用している場合にshims-vue.d.tsファイルが存在しな場合は下記のようにエラーメッセージが表示されます。デフォルトで作成されるので誤って削除しないように注意してください。

“Cannot find module ‘./App.vue’ or its corresponding type declarations. ts(2307)”

エラーメッセージが表示
エラーメッセージが表示

tsconfig.jsonファイル

typescriptをこれまで一度の使ったことがない人にとってはインストールディレクトリにtsconfig.jsonと名前の見慣れないファイルも作成されています。このファイルでtypescriptに関する設定を行うことができます。tsconfig.jsonファイルもtypescriptを利用する上で必須なファイルです。

動作確認の準備コードの整理

動作確認のコードをわかりやすくするためにApp.vueファイルとHelloWorld.vueファイルのコードを更新します。


<template>
  <HelloWorld />
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import HelloWorld from './components/HelloWorld.vue';

export default defineComponent({
  name: 'App',
  components: {
    HelloWorld
  }
});
</script>

messageはpropsではなくdataプロパティとして定義しています。dataプロパティのmsgは文字列なので型はstringですが、設定を行わなくても型推論により自動に型がstringに設定されています。


<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'HelloWorld',
  data(){
    return {
      msg:'Hello TypeScript',
    }},
});
</script>

ブラウザで確認するとHello TypeScriptが表示されます。

Hello TypeScriptの表示
Hello TypeScriptの表示

TypeScriptの動作確認

TypeScriptを設定しながら動作確認を行っていきますが基本的に行うことはいずれも共通で最初に型を設定(関数であれば引数に入る値への型、また関数を実行した後に戻り値の型)し、その型と異なる型を入力しようとしたり異なる型が戻るようなコードを記述するとエラーメッセージで教えてくれるといったものです。つまり実行時にエラーを見つけるのではなくコードを記述する際にエラーを見つけることで本番環境でのエラーの発生を抑えるというものです。

変数の型の設定方法

JavaScriptのTypeで頻繁に利用するのは、String, Number, Array, Boolean, Function, Objectの6つです。各Typeを利用するためにTypeScriptを使ってどのように型の指定を行うのか確認していきます。

JavaScriptの場合は変数を定義して各変数に値を設定するだけです。特に型に関して明示的に示すことはありません。


let city = "Tokyo"
let temp = 20;
let loading = true;

TypeScriptで型の指定を行う場合は変数の名前の後ろに:(コロン)をつけて型を指定します。cityは文字列が入るのでstring, tempは数字が入るのでnumber, loadingはtrueかfalseの値のどちらかが入るのみなのでbooleanを設定します。


let city: string = "Tokyo"
let temp: number = 20;
let loading: boolean = true;

次に配列を確認します。これまでの流れからすると配列は下記のように記述できそうな感じがします。


let cities: array = ['Tokyo','Paris','London'];

配列の場合は要素の中に入る値の型の指定を行い下記のように記述します。


let cities: string[] = ['Tokyo','Paris','London'];

stringではなくbooleanの配列の場合は下記のように記述します。


let a: boolean[] = [true,false,false];

関数の場合はどのように型を設定するか確認するために引数に入れた2つの値を足してその結果を戻す関数addを使います。関数を見ると型を指定できそうな箇所が2つあることがわかります。1つは引数、2つ目は関数から返される値です。


function add(a,b){
  return a + b;
}

TypeScriptで型を指定すると下記のように記述することができます。add関数はシンプルなのであまり有益性は感じないかもしれませんが内部の処理が複雑な場合はadd関数はa, bの数値を入れると数値が戻ってくると型の指定を見ればわかるのでadd関数を再利用する際に役立てることができます。TypeScriptを利用するとコードの可読性がよくなるといわれているのはこのことからもわかります。


function add(a: number,b: number): number{
  return a + b;
}

computedプロパティ

TypeScriptがどのようにcomputedプロパティに影響するかを確認するためにdataプロパティに設定した文字数を表示するcomputedプロパティgetLengthを追加します。


  computed:{
    getLength() {
      return this.msg.length
    }
  }

TypeScriptを利用している場合はgetLengthメソッドの戻り値の型の設定を行う必要があります。msgの文字列の長さを計算する関数なので戻り値は数値となるため設定する型はnumberになります。下記のように関数の戻り値の型を設定することができます。


  computed:{
    getLength(): number {
      return this.msg.length
    }
  }

戻り値の型をnumberに設定した後に動作確認のためthis.msg.lengthからlengthを一時的に削除してください。削除することによりgetLengthの戻り値が数字から文字列になるためエラーが発生します。

Type “string”はtype “number”に割り当てることができないというメッセージが表示されます。

型のエラー
型のエラー

TypeScriptではファイルを保存する前にエディター上にメッセージが表示されるのでコードの間違いにすぐに気づくことができます。

TypeScriptで設定した型とgetLengthで戻される値の型が一致しない場合にどのようなエラーが表示されるか確認することができました。

型推論

dataプロパティでmsgの型を指定しませんでしたがエラーが出力されず問題なく動作します。これはdataプロパティのmsgに文字列を設定した時にTypeScriptが型を自動でstringと判断してくれるためです。msgのように明確に型がわかる場合は型を指定する必要がありません。これを型推論と呼びます。

msgの型をstringと判断しているので、methodsを利用してmsgの値を数値に変更しようとするとエラーメッセージが表示されます。


  methods:{
    changeMsg() {
      return this.msg = 3
    }
  },

Type “numberr”はtype “string”に割り当てることができないというメッセージが表示されます。

型推論の動作確認
型推論の動作確認

changeMsgメソッドに引数がある場合を確認します。引数のmsgがstringの場合はdataプロパティの型もstringなので問題もなく処理ができます。


  methods:{
    changeMsg(msg: string) {
      return this.msg = msg
    }
  },

引数のmsgにstringとは異なる型であるnumberを設定した場合には、Type ‘number’ is not assignable to type ‘string’のメッセージが表示されます。


  methods:{
    changeMsg(msg: number) {
      return this.msg = msg
    }
  },

dataプロパティへの明示的な型設定

dataプロパティにmsgを追加し文字列を設定すると型推論により自動に型が設定されました。明示的に型を設定したい場合はasを利用して設定することができます。


data() {
  return {
    msg: 'Hello TypeScript' as string,
  };
},

設定した値(下記では文字列)とは異なる型を設定した場合はエラーメッセージが表示されます。


data() {
  return {
    msg: 'Hello TypeScript' as number,
  };
},
異なる型を設定した場合
異なる型を設定した場合

msgに文字列だけではなく数値も入れたいという場合はunion型で記述することができます。この場合はmsgの値が文字列でも数値でもエラーメッセージは表示されません。


data() {
  return {
    msg: 'Hello TypeScript' as number | string,
  };
},

changeMsgメソッドの引数にもunion型を設定することができます。dataプロパティにnumberとstringを設定した後にchangeMsgの引数に必ずnumberとstringを設定する必要ではなく片方のstringだけでも問題ありません。


  methods:{
    changeMsg(msg: number | string) {
      return this.msg = msg
    }
  },

boolenだけ設定した場合や片方をbooleanに変更した場合にはエラーメッセージが表示されます。


  methods:{
    changeMsg(msg: string | boolean) {
      return this.msg = msg
    }
  },

メソッドの型

computedプロパティの場合は戻り値の型の設定を行いました。メソッドは型を行わなくてもエラーメッセージは表示されていません。メソッドの名前にカーソルを合わせると戻り値の型が型推論によって自動で設定されていることがわかります。戻り値がないのでvoidになっています。

メソッドの型の確認
メソッドの型の確認

changeMsgでreturnでmsgを戻すように変更を行います。


methods: {
  changeMsg(msg: string) {
    this.msg = msg;
    return msg;
  },
},

再度メソッドにカーソルを合わせるとメソッドの戻り値がvoidからstringに変わっていることがわかります。

メソッドの戻り値の型の確認
メソッドの戻り値の型の確認

メソッドの戻り値に明示的に型を設定することもできます。


methods: {
  changeMsg(msg: string): string {
    this.msg = msg;
    return msg;
  },
},

メソッドによって戻される型と戻り値の型を異なる型に変更するとメッセージが表示されます。戻り値のnumber側ではなくreturn msgにメッセージ表示されます。


methods: {
  changeMsg(msg: string): number {
    this.msg = msg;
    return msg;
  },
},
メソッドの戻り値のエラー
メソッドの戻り値のエラー

データオブジェクトへの型設定

オブジェクトのような複数のプロパティを持つ場合は型の宣言にinterfaceを利用することができます。Interfaceは設計図のようなもので実際に値は入っていませんが、interfaceを見ることでどのようなプロパティと型でオブジェクトが構成されているかがわかります。

まずInterfaceでBookを宣言します。title, author, yearの3つのプロパティが含まれておりstringとnumberの型を持っています。


interface Book {
  title: string;
  author: string;
  year: number;
}

データプロパティbookを追加します。追加しましたが型の設定は何も行っていません。この状態では追加したInterfaceのBookとの関連は何もありません。


export default defineComponent({
  name: 'HelloWorld',
  data() {
    return {
      book: {
        title: 'Vue 3 Guide',
        author: 'Vue Team',
        year: 2020
      }
    }
  }
});

追加したbookを下記のようにtemplateタグに設定しても問題なく表示されます。


<template>
    <h1>{{ book.title }}/{{ book.author }}/{{ book.year }}</h1>
</template>
bookの中身を表示
bookの中身を表示

メソッドを利用してyearを2020の数値から文字列に変更してみましょう。型推論によりbookのyearは数値の型が設定されているので文字列に変更しようとするとエラーが発生します。

エラ〜メッセージが表示
エラ〜メッセージが表示

しかし、bookについては型設定を行っていないため初期値を文字列にすると変更は可能となります。初期値を文字列に変更すると今度は数値に変更しようとするとエラーが発生します。


export default defineComponent({
  name: 'HelloWorld',
  data() {
    return {
      book: {
        title: 'Vue 3 Guide',
        author: 'Vue Team',
        year: "2020"
      }
    }
  },
  methods:{
    changeYear(){
      this.book.year = "2021"
    }
  }
});

ここまではinterface Bookを利用していなかったのでInterfaceを利用していない場合の型に対する処理を理解することできました。次はinterfaceを利用して設定を行いinterfaceを設定しない場合との違いを理解していきましょう。bookのオブジェクトの最後にas Bookを追加しています。


data() {
  return {
    book: {
      title: 'Vue 3 Guide',
      author: 'Vue Team',
      year: 2020
    } as Book
  }
},

bookに対してinterface Bookの型が設定されたので、もしyearの2020を文字列に変更しようとするとエラーメッセージが表示されます。interface Bookを設定することで初期値を設定する際にエラーメッセージが表示されるため誤った初期値を設定することがなくなります。

エラーメッセージ
エラーメッセージ

初期値を設定後にメソッドを使ってyearに文字列を設定しようとするとエラーが発生します。数値の場合は変更を行っても型が変わらないためエラーは発生しません。

型エイリアス

型に名前をつけることができるtypeというものがあり型エイリアスと呼ばれます。エイリアスという名前がついているように型に別名をつける時に利用することができます。numberにYearという別名をつけています。typeで作成した別名を使って型を設定することができます。


type Year = number;
const birthday: Year = 2000;

typeはinterfaceと同様にオブジェクトに対しての型を設定することができます。interfaceとは書き方が似ていますがtypeでは=(イコール)が使われたり、最後の閉じるカーリーブレースの後ろに;(コロン)がつきます。


type Book =  {
  title: string;
  author: string;
  year: number;
};

typeは下記のように決められた複数の値を持つ変数の型設定に利用することができます。injectにはpfizer, moderna, astrazeneca以外を入れようとするとエラーになります。


Type VACCINE = 'pfizer' | 'moderna' | 'astrazeneca';
const inject: VACCINE = 'pfizer'

propsの場合

dataプロパティのオブジェクトの設定方法が確認できたので次はpropsの場合はどのように型を設定していくのか確認していきましょう。

propsについてはVue自身もpropsでtypeの設定を行うことができます。まずVueのpropsのtypeを利用して動作確認を行います。

HelloWorld.vueでpropsのmsgを設定します。型はStringで設定します。


<template>
    <h1>{{ msg }}</h1>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'HelloWorld',
  props:{
    msg:String
  }
});
</script>

App.vueでpropsのmsgにHello TypeScriptを設定します。


<template>
  <HelloWorld msg="Hello TypeScript" />
</template>

ブラウザで確認するとHello TypeScriptが表示されます。

propsを利用して表示
propsを利用して表示

App.vue側からmsgに数値を入れるとブラウザのコンソールにエラーメッセージが表示されます。ブラウザのデベロッパーツールを利用して確認してください。


<template>
  <HelloWorld :msg="100" />
</template>
コンソールにエラメッセージ表示
コンソールにエラメッセージ表示
数値でpropsを渡す場合は:(コロン)を忘れずに付与してください。付与しない場合は文字列として渡されます。

Vueの型設定ではなく、TypeScriptでpropsの型を設定する場合はPropTypeをimportし下記のように設定を行います。設定を行うとVueのtypeと同様に動作します。App.vue側で数値を入力すると同じメッセージが表示されます。


<script lang="ts">
import { defineComponent, PropType } from 'vue';

export default defineComponent({
  name: 'HelloWorld',
  props:{
    msg:{
      type: String as PropType<string>,
    }
  }
});
</script>

propsで受け取る値が数値である場合は下記のように設定を行うことができます。


type: Number as PropType<number>,

オブジェクトのprops

Propsが先ほどのようにStringやNumberではなくObjectの場合はInterfaceを利用して型の設定を行うことができます。

propsを受け取る側のHelloWorld.vueで受け取るpropsデータのInterfaceを作成します。firstName, lastName, ageの3つのプロパティを持ちます。


interface User {
  firstName: string;
  lastName: string;
  age: number;
}

propsの名前はuserでPropTypeを利用して型を設定します。Vueのpropsの機能でrequireも設定しています。requiredは親から渡されるpropsが必須の場合に設定を行います。


props: {
  user: {
    type: Object as PropType<User>,
    required: true,
  }
},

受け取ったpropsはcomputedプロパティを利用してブラウザに表示するため、fullNameという関数を追加し、戻り値の型はstringに設定します。


computed: {
  fullName(): string {
    return this.user.firstName + ' ' + this.user.lastName
  }
}

templateタグの中で設定したcomputedプロパティfullNameを表示させます。一緒にthis.user.ageも表示させます。


<template>
    <h1>{{ fullName }} {{ this.user.age }}</h1>
</template>

propsを渡す親側のコンポーネントApp.vueの設定を行います。dataプロパティでpersonを設定し、firstName, lastName, ageを設定しています。personデータをcomputedプロパティuserに設定します。


data(){
  return {
    person:{
      firstName: 'John',
      lastName: 'doe',
      age: 30,
    }
  }
},
computed:{
  user(): object {
    return this.person;
  }
}

templateタグの中でv-bindを利用してcomputedプロパティのuserオブジェクトをHelloWorldコンポーネントに渡します。


<template>
  <HelloWorld :user="user"/>
</template>

ブラウザで確認するとユーザ名とageを確認することができます。

ユーザ名とageが表示
ユーザ名とageが表示

TypeScriptで型の設定を行っているので、App.vue側から渡すデータの型が受け取る側の設定した型と異なる場合にデータを送信するとエラーメッセージが表示されます。下記ではageを文字列に変更しています。コンパイル時にType ‘string’ is not assignable to type ‘number’.というエラーが表示されます。


    person:{
      firstName: 'John',
      lastName: 'doe',
      age: '30',
    }
コンパイルはnpm run serveを実行している中でファイルの保存を行うと自動で行われます。

InterfaceをExportする

HelloWorldで作成したInterfaceはExportを行うことで親コンポーネントでもそのInterfaceを利用することができます。

別のコンポーネントファイルで記述したinterfaceだから利用できるわけではなく別ファイルでinterfaceを定義してexportを行えば他のコンポーネントでimportすることができます。

親と子のコンポーネントで同じinterfaceの定義を共有することで親コンポーネントで初期値を設定する際にエディターのエラーメッセージで型の間違いを確認することができます。

exportをinterfaceの前に追加します。


export interface User {
  firstName: string;
  lastName: string;
  age: number;
}

親コンポーネントのApp.vueでexportしたInterfaceをimportします。これでApp.vueでもInterfaceのUserを設定することができます。


import HelloWorld, { User } from './components/HelloWorld.vue';

InterfaceのUserをcomputedプロパティのuserで利用します。


computed:{
  user(): User {
    return this.person;
  }
}

この状態で初期値のageの値を数値から文字列に変更すると下記のエラーメッセージが表示されます。

エディターのエラーメッセージ
エディターのエラーメッセージ

computedプロパティの戻り値にUserを設定しましたが、今回はdataプロパティのオブジェクトも同じ型なのでdataプロパティにも型を設定することができます。


data(){
  return {
    person:{
      firstName: 'John',
      lastName: 'doe',
      age: 30,
    } as User
  }
},

firstName、lastNameを数値、ageを文字列に変更するとすぐにエラーが表示されます。

少ない例でのTypeScriptの設定例でしたがVueのアプリケーションを構築する上で必須の要素であるdata, props, computed, methodsでのTypeScriptを利用方法を確認することができました。

Composition APIとTypeScript

これまではOptions APIを利用してTypeScriptの設定方法を確認してきました。Vue3からComposition APIを利用してコードを記述することができます。ここからはComposition APIを利用した場合のTypeScriptの設定方法を説明していきます。まだComposition APIに慣れていない人もいると思うのでComposition APIの説明も各所で入れています。

Reactiveでの設定


<template>
  <div class="app">
    {{ state.msg }}
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive } from 'vue';

export default defineComponent({
  name: 'App',
  components: {},
  setup() {
    const state = reactive({
      msg: 'Hello TypeScript',
    });
    return {
      state,
    };
  },
});
</script>

setup関数を追加してdataプロパティをreactiveを利用して設定します。

ブラウザ上にはHello TypeScriptが表示されます。dataプロパティにmsgを設定し型を設定していますせんが型推論によってstringが設定されていることが確認できます。

reactiveでdataプロパティを設定
reactiveでdataプロパティを設定

型を明示的に指定したい場合はasを利用して行います。


const state = reactive({
  msg: 'Hello TypeScript' as string,
});

stringからnumberに変更するとエラーメッセージが表示されます。

Reactiveを利用している場合はtoRefsによってstateをrefに変換することができます。refに変換することでstate.msgをmsgに変更することができます。型への影響はありません。


<template>
  <div class="app">
    {{ msg }}
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';

export default defineComponent({
  name: 'App',
  components: {},
  setup() {
    const state = reactive({
      msg: 'Hello TypeScript' as string,
    });
    return {
      ...toRefs(state),
    };
  },
});
</script>

stateにはsetup関数の中でアクセスすることができます。state.msgに数値を設定するとエラーメッセージが表示されます。


setup() {
  const state = reactive({
    msg: 'Hello TypeScript' as string,
  });

  state.msg = 25;
state.msgに数値を設定
state.msgに数値を設定

reactiveはジェネリックを使って下記のように型を設定することができます。


  const state = reactive<{ msg: string }>({
    msg: 'Hello TypeScript',
  });

さらにinterfaceを利用して型を分けることができます。


interface State {
  msg: string;
}
//略
    const state = reactive({
      msg: 'Hello TypeScript',
    });

refでの設定

reactiveからrefに変更を行いdataプロパティmsgの設定を行います。


<template>
  <div class="app">
    {{ msg }}
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'App',
  components: {},
  setup() {
    const msg = ref('Hello TypeScript');
    return {
      msg,
    };
  },
});
</script>

上記のように設定を行うと型推論によりstringが設定されます。

型推論によりstringに設定

refの場合に型を設定する場合はasではなく下記のようにジェネリックで型を設定することができます。


const msg = ref('Hello TypeScript');

refで定義したデータにアクセスする場合は.valueをつける必要があります。


msg.value="Hello Vue 3";

refでオブジェクトの設定

オブジェクトの型を設定する場合はinterfaceを利用します。interfaceの設定についてはOptions APIの時と同じです。


interface Book {
  title: string;
  author: string;
  year: number;
}

refで型のBookで作成する場合はジェネリックで設定します。interfaceで設定した型で初期値を設定することができます。


const book = ref<Book>({
  title: 'Vue 3 Guide',
  author: 'Vue Team',
  year: 2020,
});

異なる型を設定した場合にはエラーメッセージが表示されます。

型とは異なる値を設定した場合
型とは異なる値を設定した場合

外部リソースからbook情報を取得する場合には配列で保存します。配列の場合は以下のように型を設定することができます。


const books = ref([
  {
    title: 'Vue 3 Guide',
    author: 'Vue Team',
    year: 2020,
  },
  {
    title: 'Vite Guide',
    author: 'Vue Team',
    year: 2021,
  },
]);

propsの場合

propはComposition APIでもOptions APIの場合でも利用方法が同じなので型の設定も同じです。HelloWorldコンポーネントを使って確認します。

HelloWorld.vueファイルに以下を記述します。propsに型を設定する場合はPropTypeをimportしてます。


<template>
  <h1>{{ msg }}</h1>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue';

export default defineComponent({
  name: 'HelloWorld',
  props: {
    msg: {
      type: String as PropType<string>,
    },
  },
});
</script>

refで定義したmsgプロパティをpropsとしてHelloWorldコンポーネントに渡しています。


<template>
  <div class="app">
    <hello-world :msg="msg" />
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';
import HelloWorld from './components/HelloWorld.vue';

export default defineComponent({
  name: 'App',
  components: {
    HelloWorld,
  },
  setup() {
    const msg = ref('Hello TypeScript');

    return {
      msg,
    };
  },
});
</script>

メソッド(関数)の場合

Options APIではmethodsの中に関数を記述していましたがComposition APIではsetup関数の中に関数を作成することができます。msgの値を変更するとchangeMsg関数を追加する。msg.valueで値を変更することが可能です。追加した関数をtemplateタグの中で利用するためにはreturnのオブジェクトの中にchangeMsgを含める必要があります。


setup() {
  const msg = ref('Hello TypeScript');

  const changeMsg = () => {
    msg.value = 'Hello Vue';
  };

  return {
    msg,
    changeMsg,
  };
},

型推論によってmsgの型がstringに設定されているので上記のコードでは問題はありませんがmsg.valueの値を数値に変更するとエラーメッセージが表示されます。

数値を変更するとエラー
数値を変更するとエラーC

Composition APIの場合もreactive, ref, props, 関数でのTypeScriptの設定方法の基礎を確認することができました。