9 |
10 |
11 |
35 |
36 |
41 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 |
404 |
405 |
Hello {{name}}{{exclamationMarks}}
406 |
407 |
408 |
409 |
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 `