├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── index.html ├── package.json ├── src ├── components │ ├── Hello.spec.ts │ ├── Hello.vue │ └── HelloDecorator.vue ├── index.ts └── vue-shims.d.ts ├── tsconfig.json ├── tslint.json ├── webpack.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | .DS_Store -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.associations": { 4 | "*.vue": "vue" 5 | }, 6 | "tslint.autoFixOnSave": true 7 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repo is deprecated, it was created in the days before Vue shipped with TypeScript out of the box. Now the 2 | best path to get started is through [the official CLI](https://cli.vuejs.org). We'll keep this repo around as 3 | a useful archive. 4 | 5 | --- 6 | 7 | # TypeScript Vue Starter 8 | 9 | This quick start guide will teach you how to get [TypeScript](http://www.typescriptlang.org/) and [Vue](https://vuejs.org) working together. 10 | This guide is flexible enough that any steps here can be used to integrate TypeScript into an existing Vue project. 11 | 12 | # Before you begin 13 | 14 | If you're new to Typescript and Vue, here are few resources to get you up and running: 15 | 16 | ## TypeScript 17 | * [Up and Running with TypeScript](https://egghead.io/courses/up-and-running-with-typescript) 18 | * [TypeScript 5 Minute Tutorial](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html) 19 | * [Documentation](http://www.typescriptlang.org/docs/home.html) 20 | * [TypeScript GitHub](https://github.com/Microsoft/TypeScript) 21 | 22 | ## Vue 23 | * [Vuejs Guide](https://vuejs.org/v2/guide/) 24 | * [Vuejs Tutorial](https://www.youtube.com/playlist?list=PL4cUxeGkcC9gQcYgjhBoeQH7wiAyZNrYa) 25 | * [Build an App with Vue.js](https://scotch.io/tutorials/build-an-app-with-vue-js-a-lightweight-alternative-to-angularjs) 26 | 27 | 28 | 29 | # Initialize your project 30 | 31 | Let's create a new package. 32 | 33 | ```sh 34 | mkdir typescript-vue-tutorial 35 | cd typescript-vue-tutorial 36 | ``` 37 | 38 | Next, we'll scaffold our project in the following way: 39 | 40 | ```txt 41 | typescript-vue-tutorial/ 42 | ├─ dist/ 43 | └─ src/ 44 | └─ components/ 45 | ``` 46 | 47 | TypeScript files will start out in your `src` folder, run through the TypeScript compiler, then webpack, and end up in a `bundle.js` file in `dist`. 48 | Any components that we write will go in the `src/components` folder. 49 | 50 | Let's scaffold this out: 51 | 52 | ```shell 53 | mkdir src 54 | cd src 55 | mkdir components 56 | cd .. 57 | ``` 58 | 59 | Webpack will eventually generate the `dist` directory for us. 60 | 61 | # Initialize the project 62 | 63 | Now we'll turn this folder into an npm package. 64 | 65 | ```shell 66 | npm init 67 | ``` 68 | 69 | You'll be given a series of prompts. 70 | You can use the defaults except for your entry point. 71 | You can always go back and change these in the `package.json` file that's been generated for you. 72 | 73 | # Install our dependencies 74 | 75 | Ensure TypeScript, Webpack, Vue and the necessary loaders are installed. 76 | 77 | ```sh 78 | npm install --save-dev typescript webpack webpack-cli ts-loader css-loader vue vue-loader vue-template-compiler 79 | ``` 80 | 81 | Webpack is a tool that will bundle your code and optionally all of its dependencies into a single `.js` file. 82 | While you don't need to use a bundler like Webpack or Browserify, these tools will allow us to use `.vue` files which we'll cover in a bit. 83 | 84 | We didn't need to [add `.d.ts` files](https://www.typescriptlang.org/docs/handbook/declaration-files/consumption.html), but if we were using a package which didn't ship declaration files, we'd need to install the appropriate `@types/` package. 85 | [Read more about using definition files in our documentation](https://www.typescriptlang.org/docs/handbook/declaration-files/consumption.html). 86 | 87 | # Add a TypeScript configuration file 88 | 89 | You'll want to bring your TypeScript files together - both the code you'll be writing as well as any necessary declaration files. 90 | 91 | To do this, you'll need to create a `tsconfig.json` which contains a list of your input files as well as all your compilation settings. 92 | Simply create a new file in your project root named `tsconfig.json` and fill it with the following contents: 93 | 94 | You can easily create `tsconfig.json` this command. 95 | 96 | ``` 97 | tsc --init 98 | ``` 99 | 100 | ```json 101 | { 102 | "compilerOptions": { 103 | "outDir": "./built/", 104 | "sourceMap": true, 105 | "strict": true, 106 | "noImplicitReturns": true, 107 | "module": "es2015", 108 | "moduleResolution": "node", 109 | "target": "es5" 110 | }, 111 | "include": [ 112 | "./src/**/*" 113 | ] 114 | } 115 | ``` 116 | 117 | Notice the `strict` flag is set to true. 118 | At the very least, TypeScript's `noImplicitThis` flag will need to be turned on to leverage Vue's declaration files, but `strict` gives us that and more (like `noImplicitAny` and `strictNullChecks`). 119 | We strongly recommend using TypeScript's stricter options for a better experience. 120 | 121 | # Adding Webpack 122 | 123 | We'll need to add a `webpack.config.js` to bundle our app. 124 | 125 | ```js 126 | var path = require('path') 127 | var webpack = require('webpack') 128 | const VueLoaderPlugin = require('vue-loader/lib/plugin') 129 | 130 | module.exports = { 131 | entry: './src/index.ts', 132 | output: { 133 | path: path.resolve(__dirname, './dist'), 134 | publicPath: '/dist/', 135 | filename: 'build.js' 136 | }, 137 | module: { 138 | rules: [ 139 | { 140 | test: /\.vue$/, 141 | loader: 'vue-loader', 142 | options: { 143 | loaders: { 144 | // Since sass-loader (weirdly) has SCSS as its default parse mode, we map 145 | // the "scss" and "sass" values for the lang attribute to the right configs here. 146 | // other preprocessors should work out of the box, no loader config like this necessary. 147 | 'scss': 'vue-style-loader!css-loader!sass-loader', 148 | 'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax', 149 | } 150 | // other vue-loader options go here 151 | } 152 | }, 153 | { 154 | test: /\.tsx?$/, 155 | loader: 'ts-loader', 156 | exclude: /node_modules/, 157 | options: { 158 | appendTsSuffixTo: [/\.vue$/], 159 | } 160 | }, 161 | { 162 | test: /\.(png|jpg|gif|svg)$/, 163 | loader: 'file-loader', 164 | options: { 165 | name: '[name].[ext]?[hash]' 166 | } 167 | }, 168 | { 169 | test: /\.css$/, 170 | use: [ 171 | 'vue-style-loader', 172 | 'css-loader' 173 | ] 174 | } 175 | ] 176 | }, 177 | resolve: { 178 | extensions: ['.ts', '.js', '.vue', '.json'], 179 | alias: { 180 | 'vue$': 'vue/dist/vue.esm.js' 181 | } 182 | }, 183 | devServer: { 184 | historyApiFallback: true, 185 | noInfo: true 186 | }, 187 | performance: { 188 | hints: false 189 | }, 190 | devtool: '#eval-source-map', 191 | plugins: [ 192 | // make sure to include the plugin for the magic 193 | new VueLoaderPlugin() 194 | ] 195 | } 196 | 197 | if (process.env.NODE_ENV === 'production') { 198 | module.exports.devtool = '#source-map' 199 | // http://vue-loader.vuejs.org/en/workflow/production.html 200 | module.exports.plugins = (module.exports.plugins || []).concat([ 201 | new webpack.DefinePlugin({ 202 | 'process.env': { 203 | NODE_ENV: '"production"' 204 | } 205 | }), 206 | new webpack.optimize.UglifyJsPlugin({ 207 | sourceMap: true, 208 | compress: { 209 | warnings: false 210 | } 211 | }), 212 | new webpack.LoaderOptionsPlugin({ 213 | minimize: true 214 | }) 215 | ]) 216 | } 217 | ``` 218 | 219 | # Add a build script 220 | 221 | Open up your `package.json` and add a script named `build` to run Webpack. 222 | Your `"scripts"` field should look something like this: 223 | 224 | ```json 225 | "scripts": { 226 | "build": "webpack", 227 | "test": "echo \"Error: no test specified\" && exit 1" 228 | }, 229 | ``` 230 | 231 | Once we add an entry point, we'll be able to build by running 232 | 233 | ```sh 234 | npm run build 235 | ``` 236 | 237 | and have builds get triggered on changes by running 238 | 239 | ```sh 240 | npm run build -- --watch 241 | ``` 242 | 243 | # Create a basic project 244 | 245 | Let's create the most bare-bones Vue & TypeScript example that we can try out. 246 | First, create the file `./src/index.ts`: 247 | 248 | ```ts 249 | // src/index.ts 250 | 251 | import Vue from "vue"; 252 | 253 | let v = new Vue({ 254 | el: "#app", 255 | template: ` 256 |
257 |
Hello {{name}}!
258 | Name: 259 |
`, 260 | data: { 261 | name: "World" 262 | } 263 | }); 264 | 265 | ``` 266 | 267 | Let's check to see if everything is wired up correctly. 268 | Create an `index.html` with the following content at your root: 269 | 270 | ```html 271 | 272 | 273 | 274 | 275 | 276 |
277 | 278 | 279 | 280 | 281 | ``` 282 | 283 | Now run `npm run build` and open up your `index.html` file in a browser. 284 | 285 | You should see some text that says `Hello World!`. 286 | Below that, you'll see a textbox. 287 | If you change the content of the textbox, you'll notice how the text is synchronized between the two. 288 | 289 | Congrats! 290 | You've gotten TypeScript and Vue fully hooked up! 291 | 292 | # Adding a component 293 | 294 | As you've just seen, Vue has a very simple interface for when you need to accomplish simple tasks. 295 | When our page only needed to communicate a bit of data between two elements, it took very little code. 296 | 297 | For more complex tasks, Vue is flexible in that it supports breaking your application into *components*. 298 | [Components](https://vuejs.org/v2/guide/components.html) are useful for separating the concerns of how entities are displayed to the user. 299 | [Read up more on components from Vue's documentation.](https://vuejs.org/v2/guide/components.html) 300 | 301 | A Vue component can be declared in the following manner: 302 | 303 | ```ts 304 | // src/components/Hello.ts 305 | 306 | import Vue from "vue"; 307 | 308 | export default Vue.extend({ 309 | template: ` 310 |
311 |
Hello {{name}}{{exclamationMarks}}
312 | 313 | 314 |
315 | `, 316 | props: ['name', 'initialEnthusiasm'], 317 | data() { 318 | return { 319 | enthusiasm: this.initialEnthusiasm, 320 | } 321 | }, 322 | methods: { 323 | increment() { this.enthusiasm++; }, 324 | decrement() { 325 | if (this.enthusiasm > 1) { 326 | this.enthusiasm--; 327 | } 328 | }, 329 | }, 330 | computed: { 331 | exclamationMarks(): string { 332 | return Array(this.enthusiasm + 1).join('!'); 333 | } 334 | } 335 | }); 336 | ``` 337 | 338 | This component has two buttons and some text. 339 | When rendered, it takes an initial `name` and an `initialEnthusiasm` which is the number of exclamation marks we want to display. 340 | When we hit the `+` button, it adds an exclamation mark to the end of the text. 341 | Likewise, when we hit the `-` button, it removes an exclamation mark unless we're down to just one. 342 | 343 | Our root Vue instance can consume it as follows: 344 | 345 | ```ts 346 | // src/index.ts 347 | 348 | import Vue from "vue"; 349 | import HelloComponent from "./components/Hello"; 350 | 351 | let v = new Vue({ 352 | el: "#app", 353 | template: ` 354 |
355 | Name: 356 | 357 |
358 | `, 359 | data: { name: "World" }, 360 | components: { 361 | HelloComponent 362 | } 363 | }); 364 | ``` 365 | 366 | However, we'll note that it is fairly popular to use [Vue's *single file components*](https://vuejs.org/v2/guide/single-file-components.html). 367 | Let's try writing the above as an SFC. 368 | 369 | # Single File Components 370 | 371 | When using Webpack or Browserify, Vue has plugins like [vue-loader](https://github.com/vuejs/vue-loader) and [vueify](https://www.npmjs.com/package/vueify) which allow you to author your components in HTML-like files. 372 | These files, which end in a `.vue` extension, are single file components. 373 | 374 | There are a few things that need to be put in place to use `.vue` files with TypeScript, but luckily we're already halfway there. 375 | We already installed vue-loader earlier when we got our dev dependencies. 376 | We also specified the `appendTsSuffixTo: [/\.vue$/],` option to ts-loader in our `webpack.config.js` file, which allows TypeScript to process the code extracted from a single file component. 377 | 378 | One extra thing we'll have to do is tell TypeScript what `.vue` files will look like when they're imported. 379 | We'll do this with a `vue-shims.d.ts` file: 380 | 381 | ```ts 382 | // src/vue-shims.d.ts 383 | 384 | declare module "*.vue" { 385 | import Vue from "vue"; 386 | export default Vue; 387 | } 388 | ``` 389 | 390 | We don't need to import this file anywhere. 391 | It's automatically included by TypeScript, and it tells it that anything imported that ends in `.vue` has the same shape of the Vue constructor itself. 392 | 393 | What's left? 394 | The editing experience! 395 | One of the best features TypeScript gives us is its editor support. 396 | To leverage that within `.vue` files, we recommend using [Visual Studio Code](https://code.visualstudio.com/) with the [Vetur](https://marketplace.visualstudio.com/items?itemName=octref.vetur) plugin for Vue. 397 | 398 | Now, let's write an SFC! 399 | 400 | ```html 401 | 402 | 403 | 410 | 411 | 436 | 437 | 442 | ``` 443 | 444 | and let's import it for our root instance: 445 | 446 | ```ts 447 | // src/index.ts 448 | 449 | import Vue from "vue"; 450 | import HelloComponent from "./components/Hello.vue"; 451 | 452 | let v = new Vue({ 453 | el: "#app", 454 | template: ` 455 |
456 | Name: 457 | 458 |
459 | `, 460 | data: { name: "World" }, 461 | components: { 462 | HelloComponent 463 | } 464 | }); 465 | ``` 466 | 467 | Notice a few things about our single-file component: 468 | 469 | * We had to write ` 9 | 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-vue-starter", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "", 6 | "main": "index.js", 7 | "scripts": { 8 | "build": "webpack", 9 | "test": "jest" 10 | }, 11 | "keywords": [], 12 | "author": { 13 | "name": "Daniel Rosenwasser", 14 | "url": "https://github.com/DanielRosenwasser" 15 | }, 16 | "license": "Apache-2.0", 17 | "dependencies": { 18 | "vue": "^2.5.2", 19 | "vue-class-component": "^6.0.0", 20 | "vue-property-decorator": "^6.0.0", 21 | "vue-router": "^3.0.1" 22 | }, 23 | "devDependencies": { 24 | "@types/jest": "^23.3.2", 25 | "@vue/test-utils": "^1.0.0-beta.25", 26 | "css-loader": "^0.28.1", 27 | "jest": "^23.6.0", 28 | "ts-jest": "^23.10.3", 29 | "ts-loader": "^2.0.3", 30 | "typescript": "^2.7.2", 31 | "vue-jest": "^2.6.0", 32 | "vue-loader": "^12.0.3", 33 | "vue-template-compiler": "^2.5.2", 34 | "webpack": "^2.5.0" 35 | }, 36 | "jest": { 37 | "moduleFileExtensions": [ 38 | "js", 39 | "ts", 40 | "vue" 41 | ], 42 | "modulePaths": [ 43 | "/src", 44 | "/node_modules" 45 | ], 46 | "transform": { 47 | ".*\\.(vue)$": "/node_modules/vue-jest", 48 | ".*\\.(ts)$": "ts-jest" 49 | }, 50 | "moduleNameMapper": { 51 | "^@/(.*)$": "/src/$1" 52 | }, 53 | "globals": { 54 | "vue-jest": { 55 | "tsConfigFile": "tsconfig.json" 56 | } 57 | }, 58 | "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/components/Hello.spec.ts: -------------------------------------------------------------------------------- 1 | import Hello from '@/components/Hello.vue'; 2 | import { mount } from '@vue/test-utils'; 3 | 4 | describe('Hello.vue', () => { 5 | it('should render', () => { 6 | const name = 'World'; 7 | const initialEnthusiasm = 5; 8 | 9 | const wrapper = mount(Hello, { 10 | propsData: { 11 | name, initialEnthusiasm 12 | } 13 | }) 14 | expect(wrapper.find('.greeting').text()).toBe('Hello World!!!!!') 15 | }) 16 | }); -------------------------------------------------------------------------------- /src/components/Hello.vue: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 35 | 36 | 41 | -------------------------------------------------------------------------------- /src/components/HelloDecorator.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 35 | 36 | 41 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import HelloComponent from "./components/Hello.vue"; 3 | import HelloDecoratorComponent from "./components/HelloDecorator.vue"; 4 | 5 | let v = new Vue({ 6 | el: "#app", 7 | template: ` 8 |
9 | Name: 10 |

Hello Component

11 | 12 |

Hello Decorator Component

13 | 14 |
15 | `, 16 | data: { name: "World" }, 17 | components: { 18 | HelloComponent, 19 | HelloDecoratorComponent 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /src/vue-shims.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import Vue from "vue"; 3 | export default Vue; 4 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./built/", 4 | "sourceMap": true, 5 | "strict": true, 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "target": "es5", 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "experimentalDecorators": true 12 | }, 13 | "include": [ 14 | "./src/**/*" 15 | ] 16 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "quotemark": [ 9 | true, 10 | "single" 11 | ], 12 | "indent": [ 13 | true 14 | ], 15 | "interface-name": [ 16 | false 17 | ], 18 | "arrow-parens": false, 19 | // Pending fix for shorthand property names. 20 | "object-literal-sort-keys": false 21 | }, 22 | "rulesDirectory": [] 23 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path') 3 | var webpack = require('webpack') 4 | 5 | module.exports = { 6 | entry: './src/index.ts', 7 | output: { 8 | path: path.resolve(__dirname, './dist'), 9 | publicPath: '/dist/', 10 | filename: 'build.js' 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.vue$/, 16 | loader: 'vue-loader', 17 | options: { 18 | loaders: { 19 | // Since sass-loader (weirdly) has SCSS as its default parse mode, we map 20 | // the "scss" and "sass" values for the lang attribute to the right configs here. 21 | // other preprocessors should work out of the box, no loader config like this necessary. 22 | 'scss': 'vue-style-loader!css-loader!sass-loader', 23 | 'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax', 24 | } 25 | // other vue-loader options go here 26 | } 27 | }, 28 | { 29 | test: /\.tsx?$/, 30 | loader: 'ts-loader', 31 | exclude: /node_modules/, 32 | options: { 33 | appendTsSuffixTo: [/\.vue$/], 34 | } 35 | }, 36 | { 37 | test: /\.(png|jpg|gif|svg)$/, 38 | loader: 'file-loader', 39 | options: { 40 | name: '[name].[ext]?[hash]' 41 | } 42 | } 43 | ] 44 | }, 45 | resolve: { 46 | extensions: ['.ts', '.js', '.vue', '.json'], 47 | alias: { 48 | 'vue$': 'vue/dist/vue.esm.js' 49 | } 50 | }, 51 | devServer: { 52 | historyApiFallback: true, 53 | noInfo: true 54 | }, 55 | performance: { 56 | hints: false 57 | }, 58 | devtool: '#eval-source-map' 59 | } 60 | 61 | if (process.env.NODE_ENV === 'production') { 62 | module.exports.devtool = '#source-map' 63 | // http://vue-loader.vuejs.org/en/workflow/production.html 64 | module.exports.plugins = (module.exports.plugins || []).concat([ 65 | new webpack.DefinePlugin({ 66 | 'process.env': { 67 | NODE_ENV: '"production"' 68 | } 69 | }), 70 | new webpack.optimize.UglifyJsPlugin({ 71 | sourceMap: true, 72 | compress: { 73 | warnings: false 74 | } 75 | }), 76 | new webpack.LoaderOptionsPlugin({ 77 | minimize: true 78 | }) 79 | ]) 80 | } --------------------------------------------------------------------------------