質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

npm

npmは、Node Packaged Modulesの略。Node.jsのライブラリ・パッケージを管理できるツールです。様々なモジュールを簡単にインストールでき、自分でモジュールを作成し公開する際にも使用できます。

コンパイルエラー

コンパイルのフェーズで生成されるエラーです。よく無効なシンタックスやタイプが含まれているとき発生します。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

Q&A

0回答

1779閲覧

【webpack】Vueコンポーネント内に記載したTypeScriptのデコレータを使えない

hasshy

総合スコア102

Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

npm

npmは、Node Packaged Modulesの略。Node.jsのライブラリ・パッケージを管理できるツールです。様々なモジュールを簡単にインストールでき、自分でモジュールを作成し公開する際にも使用できます。

コンパイルエラー

コンパイルのフェーズで生成されるエラーです。よく無効なシンタックスやタイプが含まれているとき発生します。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

0グッド

0クリップ

投稿2020/08/06 12:04

編集2022/01/12 10:55

vue-cliを使用せずにwebpackの設定をしています。

Vueコンポーネント内でTypeScriptのデコレータを使いたいのですが、@が解析できず処理できません。
webpackの設定やnpmのライブラリが足りていないのでしょうか?

お手数をおかけしますがご教示をお願いいたします。

環境

モジュールバージョン
vue^2.6.11
ts-loader^8.0.2
typescript^3.9.7

エラー内容

webpackコマンドでビルドすると次のようなエラーが発生します。

ご共有までに、vue-property-decoratorのデコレータを使用してコンポーネントを使おうとしました。
vue-property-decorator|npm

ERROR in ./src/ts/components/Hello.vue?vue&type=script&lang=ts& (./node_modules/vue-loader/lib??vue-loader-options!./src/ts/components/Hello.vue?vue&type=script&lang=ts&) 11:0 Module parse failed: Unexpected character '@' (11:0) File was processed with these loaders: * ./node_modules/vue-loader/lib/index.js You may need an additional loader to handle the result of these loaders. | import { Vue, Component, Prop } from "vue-property-decorator"; | > @Component | export default class Ha

ソース

webpack.config.js

ts拡張子のローダー設定で、options.appendTsSuffixToにvueファイルを対象にする設定をしています。

const path = require('path'); const webpack = require('webpack'); const glob = require('glob'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPugPlugin = require('html-webpack-pug-plugin'); const srcDir = './src'; /** * ファイル名のみ抽出 * * @param {string} path * @return {void | BaseNode | string | *} */ const getFileName = function(path) { return path.replace(/.[^/.]+$/, ''); } const templates = []; const srcPugDir = srcDir + '/pug'; glob.sync('**/*.pug', { ignore: '**/_*.pug', cwd: srcPugDir, }).map(function(file) { templates.push(new HtmlWebpackPlugin({ template: path.resolve(srcPugDir, file), filename: getFileName(file) + '.html', })); }); const TerserPlugin = require('terser-webpack-plugin'); const workboxPlugin = require('workbox-webpack-plugin'); module.exports = { mode: 'development', entry: { 'index': './src/ts/index.ts', }, output: { path: path.resolve(__dirname, './dist'), publicPath: '/dist/', filename: 'build.js', }, plugins: [ ...templates, new HtmlWebpackPugPlugin(), new VueLoaderPlugin(), new webpack.ProgressPlugin(), new workboxPlugin.GenerateSW({ swDest: 'sw.js', clientsClaim: true, skipWaiting: false, }), ], module: { rules: [ { test: /.vue$/, loader: 'vue-loader', options: { loaders: { 'scss': [ 'vue-style-loader', 'css-loader', 'sass-loader', ], 'sass': [ 'vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax', ], }, }, }, { test: /.(ts|tsx)?$/, loader: 'ts-loader', include: [], exclude: [/node_modules/], options: { appendTsSuffixTo: [/.vue$/], }, }, { test: /.(png|jpg|gif|svg)$/, loader: 'file-loader', options: { name: '[name].[ext]?[hash]', }, }, { test: /.css$/, use: [ 'vue-style-loader', 'css-loader', ], }, { test: /.pug$/, oneOf: [ { resourceQuery: /^?vue/, use: ['pug-plain-loader'], }, { use: ['raw-loader', 'pug-plain-loader'], }, ], }, { test: /.scss$/, use: [ 'style-loader', { loader: 'css-loader', options: { url: false, sourceMap: true, importLoaders: 2, }, }, { loader: 'postcss-loader', options: { sourceMap: true, plugins: [ require('autoprefixer')({ grid: true, }), ], }, }, { loader: 'sass-loader', options: { sourceMap: true, }, }, 'import-glob-loader', ], }, ], }, resolve: { extensions: ['.tsx', '.ts', '.js', '.json', '.vue'], alias: { 'vue$': 'vue/dist/vue.esm.js', }, }, devServer: { contentBase: path.join(__dirname, 'dist'), compress: true, openPage: 'index.html', watchContentBase: true, inline: true, port: 8080, historyApiFallback: true, noInfo: true, }, performance: { hints: false, }, devtool: '#eval-source-map', optimization: { minimizer: [new TerserPlugin()], splitChunks: { cacheGroups: { vendors: { priority: -10, test: /[\/]node_modules[\/]/, }, }, chunks: 'async', minChunks: 1, minSize: 30000, name: true, }, }, }; // product設定 if (process.env.NODE_ENV === 'production') { module.exports.devtool = '#source-map'; module.exports.plugins = (module.exports.plugins || []).concat([ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"', }, }), new webpack.LoaderOptionsPlugin({ minimize: true, }), ]); module.exports.optimization.minimizer = (module.exports.optimization.minimizer || []).concat([ new UglifyJsPlugin({ sourceMap: true, }), ]); }

tsconfig.json

デコレータを有効にするために、experimentalDecoratorsは有効にしています。

json

1{ 2 "compilerOptions": { 3 /* Basic Options */ 4 "target": "es5", 5 "module": "ES2015", 6 "sourceMap": true, 7 "outDir": "./", 8 9 /* Strict Type-Checking Options */ 10 "strict": true, 11 12 /* Additional Checks */ 13 "noImplicitReturns": true, 14 15 /* Module Resolution Options */ 16 "moduleResolution": "node", 17 18 "experimentalDecorators": true 19 }, 20 "include": [ 21 "./src/**/*" 22 ] 23}

コンポーネント

デコレータを指定した元のコンポーネントです。
デコレータはvue-property-decoratorからインスタンスを作成しています。 

vue

1<template lang="pug"> 2.star 3 div.greeting Hello {{name}}{{exclamationMarks}} 4 button(@click="decrement") - 5 button(@click="increment") + 6</template> 7 8<script lang="ts"> 9import { Vue, Component, Prop } from "vue-property-decorator"; 10 11@Component 12export default class HalloDecorator extends Vue { 13 @Prop() name!: string; 14 @Prop() initialEnthusiasm!: number 15 16 enthusiasm = this.initialEnthusiasm; 17 18 increment() { 19 this.enthusiasm++; 20 } 21 22 decrement() { 23 if(this.enthusiasm > 1) { 24 this.enthusiasm--; 25 } 26 } 27 28 get exclamationMarks(): string { 29 return Array(this.enthusiasm + 1).join('!'); 30 } 31}; 32</script> 33 34<style lang="scss"> 35.star { 36 color: red 37} 38</style> 39

package.json

typescriptをコンパイルするのに必要なライブラリは全てインストールしていると思います。
webpackのコマンドは、builddevで実行できるようにしています。

{ "name": "sampleproject", "version": "1.0.0", "description": "My webpack project", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --mode=production", "dev": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --mode=development", "start": "webpack-dev-server --hot" }, "author": "", "license": "ISC", "devDependencies": { "@webpack-cli/init": "^0.2.2", "autoprefixer": "^9.8.6", "babel-plugin-syntax-dynamic-import": "^6.18.0", "cross-env": "^7.0.2", "css-loader": "^4.2.0", "eslint": "^7.6.0", "eslint-config-google": "^0.14.0", "file-loader": "^6.0.0", "glob": "^7.1.6", "html-webpack-plugin": "^4.3.0", "html-webpack-pug-plugin": "^2.0.0", "import-glob-loader": "^1.1.0", "postcss-loader": "^3.0.0", "pug": "^3.0.0", "pug-plain-loader": "^1.0.0", "raw-loader": "^4.0.1", "sass": "^1.26.10", "sass-loader": "^9.0.3", "style-loader": "^1.2.1", "terser-webpack-plugin": "^4.0.0", "ts-loader": "^8.0.2", "typescript": "^3.9.7", "uglifyjs-webpack-plugin": "^2.2.0", "vue-loader": "^15.9.3", "vue-style-loader": "^4.1.2", "vue-template-compiler": "^2.6.11", "webpack": "^4.44.1", "webpack-cli": "^3.3.12", "webpack-dev-server": "^3.11.0", "workbox-webpack-plugin": "^5.1.3" }, "dependencies": { "vue": "^2.6.11", "vue-class-component": "^7.2.5", "vue-property-decorator": "^9.0.0" } }

実行したコマンド

node_module内にあるwebpackを起動して、ビルドしています。

bash

1$ npm run dev 2// node_modules/webpack/bin/webpack.js --mode=development

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問