Vue2 + TypeScript2 -- an introductory guide

Safety check and error detection are important to development experience. Vue has gone to great lengths to provide component information when typos or mistakes happen. But we can do better, error reporting can happen at compile time. This is why we want to use TypeScript with Vue.

final result

Our goal: writing Vue in TypeScript

Goals

In this guide we will finally achieve these goals:

  • writing type safe vue instance declaration
  • writing TypeScript in Vue’s single file component
  • semantic completion in pure TypeScript file
  • type checking all TypeScript, even TypeScript in *.vue‘s script tag.

All code example can be found here, with separte commits standing for sections in this article.

Prerequisite

You have to know about TypeScript2.0 and Vue2.0. And optionally webpack’s knowledge is very useful. And of course if you’re reading this article you must have known npm.

Basic setup:

First make a new directory for your project. And then init your new project and install dependencies.

1
2
3
4
5
6
mkdir vue-ts-test
cd vue-ts-test
npm init
# pressing `enter`s and done
npm install vue --save
npm install typescript --save

Note both vue and TypeScript need to be version 2. They have both definition files baked in so it’s much easier to start up with them than before. (I still respect definitely typed!)

make html template, say, index.html:

1
2
3
<div id="app"></div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="app.js"></script>

Then in your app.ts:

1
2
3
4
5
6
7
declare var Vue: any
new Vue({
el: '#app',
render(h) {
return h('h1', 'hello world')
}
})

And compile your code:

1
./node_modules/.bin/tsc app.ts

Then open index.html in your browswer! It should give you a hello world!

Exploring Vue with TypeScript

But there are a lot of feature missing in the above example. For example, you want a build step to combine all your scripts. You want component style co-locates with template and script, yet still sytnax highlighting every thing.

Let’s use Vue’s genius single file component. It requires webpack and vue-loader

1
2
3
npm install webpack --save-dev
npm install vue-loader --save-dev
npm install css-loader --save-dev

Or you can use vue-cli

1
vue init webpack-simple vue-ts-test

You need to make vue-loader to transpile your TypeScript code. vue-ts-loader can work with vue-loader.

1
npm install vue-ts-loader --save-dev

And in webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
module.exports = {
entry: { app: './app.ts', },
output: { filename: 'app.js' },

// resolve TypeScript and Vue file
resolve: {
extensions: ['', '.ts', '.vue', '.js']
},

module: {
loaders: [
{ test: /\.vue$/, loader: 'vue' },
{ test: /\.ts$/, loader: 'vue-ts' }
],
},
vue: {
// instruct vue-loader to load TypeScript
loaders: { js: 'vue-ts-loader', },
// make TS' generated code cooperate with vue-loader
esModule: true
},
}

Now you can add write Vue code in single file component!

Suppose in app.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<h1 @click="hello">hello world</h1>
</template>

<script>
export default {
methods: {
// type annotation!
hello(): void {
alert('hello world')
}
}
}
</script>

And now you can change your app.ts into an entry file.

1
2
3
4
5
6
7
8
9
10
declare var require: any

import Vue = require('vue')
var App = require('./app.vue').default

new Vue({
el: '#app',
components: { App },
render: h => h('app')
})

Now webpack your in your application directory. You can still see the hello world? Click it should give you an alert.

Cool? And here is the source.

Toward a more type safe API

Vue has a typechecker hostile API.
We can use helper library to get a better type checking. av-ts, Awesome Vue for TypeScript, is a good start point to use.

Install it by npm, again.

1
npm install --save av-ts

Then you can change your app.vue to this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<h1 @click="hello">hello {{name}}</h1>
</template>

<script>
// import dependency
import {Vue, Component} from 'av-ts'

// decorat vue class
@Component
export default class App extends Vue {
// undecorated property will be packed in `data` option
name = 'world'
// undecorated method is just method
hello() {
alert('hello ' + this.name)
}
}
</script>

webpack will give you an error. Because decorator is still considered experimental. You need to switch it on explicitly.

Add a tsconfig.json in your directory.

1
2
3
4
5
6
7
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"experimentalDecorators": true
}
}

Now it compiles! Check it out in browser!

av-ts has several features that isn’t present in other libraries.
First, prop is defined in class, so you can access these as properties in other methods without declaring in class again. Second, data, render and lifecycle hooks are declared by decorator, which is a mark to tell you these methods are different. Plus, their names and signatures can be checked by decorators. Third, extension is easy in av-ts. You can register new decorator, and still keep type safety.

For source as a whole, you can take a look here.

Even more typesafety

Vuex is another popular state management library for vue.

Kilimanjaro is a type safe fork for Vuex.

To create a store is familiar with its original API.

1
2
3
4
5
6
7
8
9
10
11
import { create } from 'kilimanjaro'

var store =
create({count: 0}) // create a state with initial state
.getter('count', s => s.count) // a getter
.mutation('increment', s => () => s.count++) // define a mutation
.mutation('decrement', s => () => s.count--) // payload is in the second parameter parenthesis
.done()

store.commit('increment') // make a state change
// store.commit('ncrement') // typo won't compile! magic!

By heavy use of string literal type and overloading, kilimanjaro provides a similiar type safe API to vuex.
If you mistakenly commit a wrong mutation name, or the mutation payload does not have correct type, kilimanjaro will make TypeScript compiler report that for you.

There is also component helper in kilimanjaro. Check the example below

1
2
3
4
5
6
7
8
9
10
11
import {Vuex, getHelper} from 'kilimanjaro'

const { getters, commit } = getHelper(store)

@Componet
class App extends Vue {
@Vuex count: number = getters('count')
@Vuex add: Function = commit('increment')
// @Vuex typo: Function = commit('ncrement') // won't compile
// @Vuex wrong: string = getters('count') // type incompatible
}

You can get component helpers by calling getHelper. Helpers are function that take a getter/mutation/action name and return corresponding result. Given a mutation name, commit helper will return a function that, once called, changes store state. The similiar for dispatch helper.

getters is more magical. Given a getter name, it will return a symbol with correct result type. But at runtime, it will be changed to a computed accessor in the VM. The magic is done by the Vuex decorator, which is an extension of av-ts. Like mentioned above, av-ts is a very extensible library.

For a working example, you can take a look here. And Live demo is here.

Vue-router is type safe enough for pragmatic usage. It is really a well written and designed library. Designing a library that provides 100% type safety is just an overkill bringing marginal benefits at huge cost of debugging compiling error.
If vue-router cannot cater for a type paranoid, this sketch will be interesting.

Limitation

You might want even more features from TypeScript, say

  • semantic completion in single file component
  • type checking template code

go home, you're drunk

They are way way hard to implement. Semantic completion in vue file requires editor support and compiler support. None of them is easy to implement.

You can write your vm code in a separate file and import into your vue template. But that is opposite to the design philosophy of single file component. In reality, keeping vue small and clean is better choice. With small number of symbols in source file, generic completion should also work well. (at least as well as plain JavaScript)

Template code is an even more heculean task. Vue compiles its template into with(this) { ... } format (doc). with statements are not checked in TypeScript, nor flow-type. Plus, vue compiler does analyze no expression, making it impossible to do checking without reimplementing a compiler. However, again, it is no worse than plain JavaScript.

Conclusion

Vue is an elegant and lightweight library. With the integration of TypeScript, Vue can be more safe and productive!

Compared to other frameworks, you can instantly get precompiled template code, small code footprint, flat learning curve.
But when it comes to scale bigger, you might want additional help.

There are overhead to integrate TypeScript with Vue, indeed. But the error checking and refactoring help still worth your try.

Tools we used to achieve type-safety

  • TypeScript2 and Vue2, for their definition files
  • vue-ts-loader: load TypeScript in vue’s single file component
  • av-ts: wrap vue’s API in type safe way

Optionally

  • kilimanjaro: type safe vuex, requires deeper understanding of TypeScript’s advanced features.

Type Safe Routing for Vue.js

Vue router is elegant and type safe enough for most use cases. But I want to experiment how far typescript can help us.

Typesafe routing isn’t easy to do.

The easiest way to declare multiple class and instantatiate their paramater type. For example, typed-url. But this is too verbose.

Another approach is to use functional combinator. To put it simple, combinator is high order function that can abstract various operation. Routing combinators usually are a bunch of functions that can accept strings as static url segment or a function as dynamic url parameter. Both purescript and swift. But monad is too monad-y. My head just explodes.

One unique way to provide type routing is using reified generic! A demo video has illustrted how to implment it.(Spoiler: for a function with type A => Response, one can access the class by A.type and cast value by guard let param: A = .... in swift. Whoa, reification is powerful). Github repo is here: https://github.com/NougatFramework/Switchboard

Compile time reflection is ideal for routing thing. Yesod uses template haskell to do this, Example. Macro paradise!

Scala has yet another unique construct called pattern match. Tumblr’s colossus is a great example to use pattern match for type safe routing.

And of course, haskell has many type safe routing library. Check out the review for more info.

JavaScript does not have powerful constructs like macro/pattern match. Combinator is the only way to achieve type safety but for client side component based routing, declaring more functions solely for routing doesn’t feel natural. And specifically TypeScript is still too feeble to describe routing. However, by combining tagged template, function overloading (or fun-dep), and intersection type (or row polymorphism), we can still do some interesting thing. If this were written in flow-type, more interesting thing could happen.

Frankly type safety in router does not grant you much: it cannot check tempalte code, it can only help you to double check the shape of parameters in $route. It can help you to type router instance better, but requires all routes to have a name field.

This is only a sketch for type safe routing design. Useful? No. Concise? Partly. Safety outweighs ease of use? No. Maybe it’s only suitable for type safe paranoid.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
interface Route<T> {
<K extends string, C>(c: StaticRouteConfig<K, C>): Routes<StaticRouteAct<K> & C & T>
<K extends string, R, P extends R, C>(c: RouteConfig<K, R, P, C>): Routes<RouteAct<K, P> & C & T>
}

interface StaticRouteAct<K> {
(location: {name: K}): void
}

interface RouteAct<K, T> {
(location: {name: K, params: T}): void
}

interface Router<T> {
push: T
replace: T
}

interface Routes<T> {
route: Route<T>
done: () => Router<T>
}

interface StaticRouteConfig<K extends string, C> {
name: K
path: StaticPath,
children?: (p: OriginPath) => Routes<C>
component?: {$routes?: {params?: {}}}
}

interface RouteConfig<K extends string, R, P extends R, C>{
name: K
path: Path<P>,
children?: (p: PathMaker<P>) => Routes<C>
component?: {$routes?: {params?: R}}
}

interface Path<T> {
__pathBrand: T
}

interface StaticPath extends Path<{}> {
__staticPathBrand: never
}

interface PathMaker<Parent> {
<A, B>(t: TemplateStringsArray, n: A, n2: B): Path<A & B & Parent>
<A>(t: TemplateStringsArray, n: A): Path<A & Parent>
(t: TemplateStringsArray): Path<Parent>
}

interface OriginPath {
__originPathBrand: never
<A, B>(t: TemplateStringsArray, n: A, n2: B): Path<A & B>
<A>(t: TemplateStringsArray, n: A): Path<A>
(t: TemplateStringsArray): StaticPath
}

var UserComponent = {$routes: {params: { uid: '333'}}}
const unimplement: never = null as never
const route: Route<never> = unimplement
const p: OriginPath = unimplement

const rs =
route({
path: p`user/${{uid: ''}}/profile/${{pageType: ''}}`,
name: 'user',
component: UserComponent,
children: p => route({
name: 'tab',
path: p`tab/${{tab: ''}}`,
component: {$routes: {params: {uid: '123', tab: '333'}}},
})
})
.route({
path: p`post`,
name: 'post'
})
.done()

rs.push({name: 'post'})

Type Safety in Vue.js

Static typing has become a hot word in frontend land: hundreds of tweets and blogs appears on social network and XXXX weekly, rivaling type checkers compete their features with each other.

Correspondingly new frameworks have a consciousness of type safety in their API design.
Angular2 has partial type safety in ViewModel code notwithstanding template code. (There has been some efforts to pursue more type safety, though)

React has full, and strict, when checked with flow, type safety by embedding templates in JavaScript(X).

But stakeholders of Vue.js, the thirdwhee.. another popular MVVM framework , might be disappointed by Vue’s type-checker hostility…

Type Checker Hostile API

Vue provides a set of simple and elegant API via heavy use of reflection that extinguishes compiler’s type inference.

It’s not Vue’s fault. Up to now static type checkers in JavaScript land have several limitations:

  1. They cannot understand modification to objects’ type or perform key-wise type inference. (more elaboration later)
  2. Some cannot annotate function’s this type. (not in flowtype 0.30, supported in TypeScript)
  3. Some cannot annotate Type Property Type. (not in TypeScript until #10425 is merged, flowtype has undocumented $Magic type like $Keys, $Record)

For point1, an example will elucidate itself.

Suppose we are going to provide a type definition file for Vue’s config option.

1
2
3
4
5
6
7
8
interface VueConfig<D, P, PD, C, M, W> {
data?: D
props?: P
propData?: PD
computed?: C & {[k: string]: (this:D & P & C & M ) => {}}
methods?: M & {[k: string]: (this:D & P & C & M) => {}}
watch?: W & {[k: string]: (this:D & P & C & M) => {}}
}

This is not very precise but does highlight some basic idea of Vue’s API. computed is a field of which the value is a function with this pointed to the object that has mixed in data, props and method. this in Vue’s option is an object made out of reflection.

Then we will write some function like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function getVue<D, P, PD, C, M, W>(opt: VueConfig<D, P, PD, C, M, W>): D & P & C & M
{
return null // placeholder
}

let a = getVueConfig({
data: {
a: 123,
},
watch: {
a: function() {
console.log(this.a) // oops
}
}
})

Compiler will complain about this.a in the watch function. Why? this cannot be inferred. To infer this, compiler will have to first infer D, P, C, M respectively. To infer D & P & C & M, compiler will have to first infer the whole expression for resolving all the type arguments. But to infer the whole expression we need first infer watch, where we need to infer this. So comes a recursion. Compiler cannot be too eager to infer type otherwise it will jump into a recursion trap. Sloth is a virtue here, even Betelgeuse cannot blame.

Alternative API

Vue’s original API is doomed to be hard to infer. However, we can build a thin layer of wrapper to leverage type checkers.

I have two alternatives to present here. One is chaining DSL, a novel approach to induct type checking and inference into Vue. The other alternative is more established and angular like: class decorator.

Chaining DSL

We can work around the recursion problem by nudging compiler to do more diligent work. Because every method/function call return a new type symbol, we can use it to escape from recursion trap:

Definition:

1
2
3
4
5
6
declare class VueTyped<T> {
data<D>(d: D): VueTyped<T & D>
method<M extends {[k:string]: (this: T) => any}>(m: M): VueTyped<T & M>
get(): T
static new(): VueTyped<{}>
}

Usage:

1
2
3
4
5
6
VueTyped.new()
.data({msg: 'hehehe'})
// return a new type symbol with field `msg`
.method({method() {return 'hello: ' + this.msg}})
// create a new type symbol with `msg` and `method`
.get() // type as {msg: string, method(): string}

Quick explanation. data has a signature like data<D>(d: D): VueTyped<D & T>. The intersection type in return position mocks mixin behavior. method<M extends {[k:string]: (this: T) => any}>(m: M): VueTyped<T & M> is more complicated. Parameter M is required for compiler to garner properties of option passed to method call. M is bounded by a constraint that every function in option must have this typed as the object we defined previously and that only defined property can be accessed via this. The final returning intersection type acts the same as data.

Note: method does not work for current TypeScript. Probably it is a bug

But step-wise inference still cannot resolve watch and computed property. $Keys magic type or keysof type does not exist in TS yet! Meanwhile, flowtype does not support this-typed function.

computed option is even harder to handle. There is no way to define this type in a getter/setter method. If we do not pass a getter/setter but a plain function as value, we cannot merge the computed properties into the resulting object.

A verbose workaround is forward reference in type annotation:

1
2
3
4
5
6
7
8
var a = VueTyped.new()
.data({ msg: 'hehe' })
.computed({
get computed(this: typeof a) { // forward reference
return this.msg + ' from WET computed!'
}
})
.get()

It’s not DRY.

Furthermore, this approach does not support language service feature like “looking for definition” or “finding all usage” because intersection type needs casting in implementation to work.

Irreparable! Irredeemable! Irremediable!

However, this approach has some benefits. First, it is easier to extend its functionality. If one would like to add vuex field in option, it just requires defining a new method. It also prevents cyclic dependency because you cannot use fields before declaring. The API itself is akin to its original version, and thusly the implementation is very thin.

Class Decorator

This approach is much more conventional, and is discussed broadly in Vue’s issue.

The basic idea is to define as many methods as possible in a class and to decorate fields to add Vue specific logic.

One exceptional API is provided by itsFrank’s vue-typescript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import * as Vue from 'vue'
import { VueComponent, Prop } from 'vue-typescript'

@VueComponent
class MyComponent {
@Prop someProp:string;

@Prop({
type: String
})
someDefaultProp:string = 'some default value';

@Prop someObjProp:{some_default:string} = {some_default: 'value'}; //vue-typescript makes sure to deep clone default values for array and object types

@Prop someFuncProp(){ //defined functions decorated with prop are treated as the default value
console.log('logged from default function!');
}

someVar:string = 'Hello!';

doStuff() {
console.log('I did stuff');
}
}

This TSish approach enables more capability of compiler tooling such as usage finding and definition lookup. Class decorator also guarantees every method’s this correctly points to class instance, which cannot be achieved in Chaining DSL approach.

With higher abstraction comes more confusion. Indeed, class decorator smooths out the discrepancy between Vue and type checker. But syntactically its API is much further from Vue’s original one. Adding new API is also harder because every decorator is hard coded in VueComponent decorator’s code. For example, adding @vuex is almost impossible without rolling out a new VuexComponent. It also cannot transform all Vue’s API, such as watch and computed: { cache: false }, into idiomatic TypeScript, leaving some orifices in type safety.

I have an alternative API bike shedding but not ready to present. Maybe I will try it later.

Conclusion

This article presents type-safety problem in Vue and two ways to mitigate the problem. Rewritten in ES015 and type-checked by one of the most advanced type checkers, Vue is designed in ES5 era and, satirically, is still designed for ES5 code.

Vue doesn’t come with type safety in mind. But this is might be a mirroring of some part in the community where some developers have almost kind of Stockholm Syndrome: they encounter so many type unsafe ordeals that they are very happy and proud with their lavish use of reflection which backfires to themselves.

Yet one should always keep a leery eye a Static Typist‘s maniacal malarkey. Static typing system works the same way as BDSM: the more constraints, the more pleasure. Once having tasted the relish of bondage, a bottom will avariciously demand more complex tricks and more powerful constraints from typing system. That urge is so strong that the bottom loses incentives to lumber out of the fifty shades of types.

A collection of nodejs CDN for Celestial Imperial

Living inside of the Great Fire Wall is a so onerous ordeal that survival guides can be compiled into an anthology.

Here is how to configure various popular open source project to use their CDN in Ch******na.

Plenty of nodejs project provide environment variable to configure mirror/CDN to download source code or binary.

nvm / nodejs

Ref

1
NVM_NODEJS_ORG_MIRROR=https://npm.taobao.org/mirrors/node nvm install 4

npm

Ref

1
npm --registry=https://registry.npm.taobao.org install koa

Kudos to fengmk2

PhantomJS

Ref

1
PHANTOMJS_CDNURL=https://npm.taobao.org/dist/phantomjs npm install phantomjs --registry=https://registry.npm.taobao.org --no-proxy

Kudos to fengmk2, again!

node-sass

Ref

1
SASS_BINARY_SITE=https://registry.npmjs.org/node-sass npm install node-sass

Kudos, again and again, to fengmk2

Docker

Ref

Install Docker

1
curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/internet | sh -

Config accelerator

You need to register an account in daocloud.io.

1
2
echo "DOCKER_OPTS=\"--registry-mirror=http://xxxxxx.m.daocloud.io\"" | sudo tee -a /etc/default/docker
sudo service docker restart

Kudos to daocloud.

Pypi

Ref

1
2
3
4
cat <~/.pip/pip.conf <<EOF
[global]
index-url = http://mirrors.aliyun.com/pypi/simple/
EOF

Kudos to aliyun

sbt

Ref

1
2
3
4
5
6
7
8
9
cat > ~/.sbt/repositories <<EOF
[repositories]
local
osc: http://maven.oschina.net/content/groups/public/
typesafe: http://repo.typesafe.com/typesafe/ivy-releases/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext], bootOnly
sonatype-oss-releases
maven-central
sonatype-oss-snapshots
EOF

Kudos to freewind

And these links is useful:
加速 SBT 下载依赖库的速度
how to make sbt jump over GFW

Gopm

Ref

It is quite clear in the doc and help command.

1
2
3
4
5
6
7

gopm get <import path>@[<tag|commit|branch>:<value>]
gopm get <package name>@[<tag|commit|branch>:<value>]

useful options:
--update, -u update package(s) and dependencies if any
--gopath, -g download all packages to GOPATH

Conclusion

Hail Aliyun! Hail fengmk2!
For the unfortunate who live in a crappy ignorant country.

Trying out dein.vim -- a dark powered plugin manager

Dark Powered Plugin

Original Japanese post here

Vim already has a lot of plugin managers.
But our Dark Vim Master has released neobundle’s successor, a brand-new plugin manger called dein.vim.

Dein.vim is a dark powered Vim/NeoVim plugin manager.

To put it in plain English, dein.vim is a plugin manager focusing on both installation performance and startup performance.
It looks like a neobunle with vim-plug’s speed, or a vim-plug with neobunle’s feature. The best of both worlds.

In this blog post I will first show minimal configuration for dein.vim, and then try to explain the dark power of dein, if you are interested in what makes dein.vim so fast.

Minimal Configuration

Though dark powered, dein.vim supports vim and neovim.

Sadly, there is no installation script for dein.vim for now.
So let’s manually install it.

First, clone dein.vim’s source.

1
2
3
mkdir -p ~/.vim/dein/repos/github.com/Shougo/dein.vim #recommended path
git clone https://github.com/Shougo/dein.vim.git \
~/.vim/dein/repos/github.com/Shougo/dein.vim

If you are familiar with neobundle/vundle, you will find dein.vim’s path so different. It is because dein uses a new approach to manage plugin’s source.

Optionally, you can backup your vimrc for profiling, as I will show later.

Then, in your init.vim or .vimrc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
set nocompatible
set runtimepath+=~/.vim/dein/repos/github.com/Shougo/dein.vim " path to dein.vim

call dein#begin(expand('~/.vim/dein')) " plugins' root path
call dein#add('Shougo/dein.vim')
call dein#add('Shougo/vimproc.vim', {
\ 'build': {
\ 'windows': 'tools\\update-dll-mingw',
\ 'cygwin': 'make -f make_cygwin.mak',
\ 'mac': 'make -f make_mac.mak',
\ 'linux': 'make',
\ 'unix': 'gmake',
\ },
\ })

call dein#add('Shougo/unite.vim')

" and a lot more plugins.....

call dein#end()

Fire up your neovim/vim, call dein.vim’s installation function

1
:call dein#install()

Wait and brew yourself a cup of (instant) coffee.
Then you can confirm your installation, for example call :Unite dein

More features

The most important feature of dein.vim is lazy load. Here are some typical usages worth mentioning.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
" lazy load on filetype
call dein#add('justmao945/vim-clang',
\{'on_ft': ['c', 'cpp']})

" lazy load on command executed
call dein#add('scrooloose/nerdtree',
\{'on_cmd': 'NERDTreeToggle'})

" lazy load on insert mode
call dein#add('Shougo/deoplete.nvim',
\{'on_i': 1})

" lazy load on function call
call dein#add('othree/eregex.vim',
\{'on_func': 'eregex#toggle'})

The last two lazy loading conditions are not available in Vimplug. While lazy loading according to mode change is very convenient.

Two Tales of Plugin Manager

From here on I will talk about dein’s internal feature. It’s my personal observation, please pardon my mistake and misunderstanding.

Vim’s plugin managers have to take two aspects into consideration: installation time and startup time.
You can read junegunn‘s blogs article, this and this, for more details on plugin manager.

dein.vim is fast because it uses dark power

JKJK. Actually, dein.vim optimizes both two performance, by following measures:

  1. parallel installation dein.vim uses either vimproc or neovim’s async job to download plugins concurrently.

  2. precomputed runtimepath dein.vim will copy all plugins’ subdirectory into a cache directory. This merges all runtime VimScript files into one directory. So dein doesn’t need to compute runtimepath on startup.

Dein.vim also ditches command usage in favor of function calling, which may also contribute to performance(I’m not sure, though).

Troubleshooting

Because dein.vim uses parallel processing, when errors occur in installation it may be hard to figure out which plugin went wrong. Usually you can see the error messages by :message after all plugin finished fetching (regardless of success or not). Or you can use dein#check_install function.

Also, precomputed cache makes modifying plugin harder. You will need to call dein#recache_runtimepath() after modification. This also applies to disabling plugins.

Lastly, if you happen to live in a country with stupid censorship which prevents github access. You will need a proxy and set g:dein$install_process_timeout to a larger value.

For more info please refer to doc.

Profiling dein’s power

Profiling vim’s startup time has a rather standard method. You can try it by backup old vimrc and compare it to dein.

1
2
nvim -u old-init.vim --startuptime neobundle.log # or change nvim to vim
nvim -u new-init.vim --startuptime dein.log

From the log, I can see dein.vim gives me 20ms startup boost! Mainly from boosting sourcing vimrc. Amazing!

If you use a lot of plugins, dein is definitely a must-try!

TypeSafe DI in TypeScript

Yet Another Type Level Arithmetics

DI is getting more popularity in JavaScript. Though, those solution is far way beyond JSR-330 compatible libraries in terms of type safety and performance.

This snippet strives to dig more type-safety from TS’ type system. However, it can hardly achieve equivalent type safety like Java’s counterpart, e.g., Dagger, Guice.

Solution

This DI snippet can, ideally, ensure every binding is resolved in compile time, which is a hard task for other DI solution. The main idea is an Injector can statically know its current binding, and judge whether dependencies of a new added binding can be already resolved by itself. Since dependency graph is a DAG, there exists a topological sorting order that every binding’s dependency can be resolved solely by those of preceding bindings . So once the injector is created and bindings are attached to it, we can assert that the dependencies can be resolved.

Say, there is a minimal example to illustrate this:

1
2
3
4
5
6
7
8
9
10
@Inject
class Clerk {}
@Inject
class Shop {constructor(c: Clerk)}
var inj = Injector.create()
.bind(Shop).toClass(Shop) // compile error here, injector cannot resolve Clerk

var inj = Injector.create()
.bind(Clerk).toClass(Clerk)
.bind(Shop).toClass(Shop) // compiles. Clerk resolved before Shop

To implement this, Injector has a shadow type Base that indicates resolved bindings. When new binding is added to injector, compiler will verify the new coming constructor/function will only depend on classes the injector has already resolved. Concretely, every argument in newly added constructor must be a subtype of Base.

1
2
3
4
5
6
7
8
9
10
type Ctor = new (...args: Base[]): T

injector<Base>
.bind(NewClass)
.toClass(NewClass as Ctor)
/*
Make sure here `toClass` is defined like
type toClass = (Ctor) => Injector<Base | T>
the union type indicates resolved type, and `T extends Base|T` holds valid
*/

Base is a large union type storing all binding types, so every resolved type is a subtype of Base. And bind will return a binder that has toClass / toFactory method which further returns an injector whose resolved binding is a union of the previous binding type and the newly added binding type. Hence, after bind ... toClass, the injector has a new class appended to its resolved type list.

The implementation and test can be found at Github Gist.

Problem

But TS’ type system does not allow a full-fledged DI in this way.

  1. First, runtime types are erased. One must annotate dependency for function in toFactory method. toClass is better because TS supports emitDecoratorMetadata. (maybe resolved in TS2.0). TS’ specific metadata implementation is also problematic. For cyclic dependent classes, at least one class’ annotation is undefined(ES3/5), or the script is crashed before it can run (ES6). Because metadata is attached to class declaration, in cyclic case there must be one class is used before it’s declared.

  2. TypeScript has a double-edged sutructural type system. To fully exploit DI’s type check, user has to add a private brand field to every injectable class. This is not a good UI, though.

  3. But even metadata is not enough. Runtime type data is first-order (in type-system’s view), that is, every type is represented by its constructor, no generic information is emitted. To work around this, token is introduced.

Token alleviates runtime type-system’s weakness, and enables binding multiple implementations to one single type. It also introduces more problem this DI wants to resolve in the first place. To work around point 1, we attached runtime types to constructor. Binding token will make type system think a type has resolved, but a following binding may not resolve it in runtime because it depends on constructor to find resolution.

1
2
3
4
5
injector
.bind(clerkToken).toClass(Clerk)
.bind(Shop).toClass(Shop) // compiles. but runtime error
// toClass will analyze Shop's signature and extract the Clerk constructor
// it can be found in type-level because Token<Clerk> enable injector to resolve Clerk, but at runtime injector can only resolve clerkToken, not Clerk

Also, tokens with same types cannot avoid this.

The workaround is, well, abusing string literal type. So every token is different at type-level. This requires users to type more types, and casts string literal from string type to string literal type. (TS’s generic inference does not have something like T extends StringLiteral so that T is inferred as string literal type)

Also, the toClass and toFactory signature should differentiate what can be resolved by constructor and by token. This is technically possible, just override these signature to support distinguishing between token and constructor. But the number of resulting overriding is exponential to the number of argument. 2 ^ n, where n is the number of arguments.

Conclusion

To fully support type-safe DI and higher performance, a compiler extension or a code generator is needed. Java’s DI relies on annotations and code generation.

Maybe Babel can do this right now. But TypeScript still needs a long way to go for a customizable emitter.

Source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import 'reflect-metadata'

type _ = {}

type ClassN<N, T> = { new (...a: N[]): T }
type FN8<A, B, C, D, E, F, G, H, R> = (a?: A, b?: B, c?: C, d?: D, e?: E, f?: F, g?: G, h?: H) => R
type CLS8<A, B, C, D, E, F, G, H, R> = { new (a?: A, b?: B, c?: C, d?: D, e?: E, f?: F, g?: G, h?: H): R}

class Token<T> {
private _tokenBrand
constructor(public name: string) {}
}
type Bindable<T> = Token<T> | ClassN<_, T>

const PARAMTYPE = 'design:paramtypes'

function getSignature<T>(cls: ClassN<T, _>): Array<Bindable<T>> { return Reflect.getOwnMetadata(PARAMTYPE, cls).slice() || [] }

const enum BindType { CLASS, VALUE, FACTORY }
class Binder<T, Base> {
dependencies: Bindable<Base>[] = []
cacheable: boolean = false
private _type: BindType
private _cls: ClassN<any, T> = null
private _val: T
private _fn: Function

constructor(private _injector: Injector<Base>) {}

private _releaseInjector() {
let injector = this._injector
this._injector = null
return injector
}

toClass<A extends Base, B extends Base, C extends Base, D extends Base, E extends Base, F extends Base, G extends Base, H extends Base>(fn: CLS8<A, B, C, D, E, F, G, H, T>, a?: Bindable<A>, b?: Bindable<B>, c?: Bindable<C>, d?: Bindable<D>, e?: Bindable<E>, f?: Bindable<F>, g?: Bindable<G>, h?: Bindable<H>): Injector<Base | T>
toClass(cls: ClassN<Base, T>, ...deps: Bindable<Base>[]): Injector<Base | T> {
this._type = BindType.CLASS
this._cls = cls
this.dependencies = getSignature(cls)
deps.forEach((d, i) => {
if (d) this.dependencies[i] = d
})
return this._releaseInjector()
}

toValue(val: T): Injector<Base | T> {
this._type = BindType.VALUE
this._val = val
this.cacheable = true
return this._releaseInjector()
}

toFactory<A extends Base, B extends Base, C extends Base, D extends Base, E extends Base, F extends Base, G extends Base, H extends Base>(fn: FN8<A, B, C, D, E, F, G, H, T>, a?: Bindable<A>, b?: Bindable<B>, c?: Bindable<C>, d?: Bindable<D>, e?: Bindable<E>, f?: Bindable<F>, g?: Bindable<G>, h?: Bindable<H>): Injector<Base | T>
toFactory(fn: (...a: Base[]) => T, ...deps: Bindable<Base>[]): Injector<Base | T> {
this._type = BindType.FACTORY
this._fn = fn
this.dependencies = deps
return this._releaseInjector()
}

resolve(deps: any[]): Promise<T> {
switch (this._type) {
case BindType.CLASS:
let cls = this._cls
return Promise.resolve(new cls(...deps))
case BindType.VALUE:
return Promise.resolve(this._val)
case BindType.FACTORY:
return Promise.resolve(this._fn(...deps))
}
}
}

export class Injector<Base> {
private _resolving = new Set<Bindable<_>>()
private _typeBinderMap = new Map<Bindable<_>, Binder<_, Base>>()
private _cache = new Map<Bindable<_>, Promise<any>>()

get<T extends Base>(cls: Bindable<T>): Promise<T> {
let binder = this._typeBinderMap.get(cls)
if (!binder) throw new NoBinding(cls['name'])

let cache = binder.cacheable ? this._cache.get(cls) : null
if (cache) {
return cache
}

if (this._resolving.has(cls)) throw new CyclicDependency(cls['name'])

this._resolving.add(cls)
let depPromise = binder.dependencies.map(dep => this.get(dep))
this._resolving.delete(cls)

let ret = Promise.all(depPromise).then(args => binder.resolve(args))
if (binder.cacheable) this._cache.set(cls, ret)
return ret
}

bind<T>(cls: Bindable<T>): Binder<T, Base> {
let binder = new Binder<T, Base>(this)
this._typeBinderMap.set(cls, binder)
return binder
}

static create(): Injector<Injector<_>> {
let inj = new Injector<Injector<_>>()
inj.bind(Injector).toValue(inj)
return inj
}
}

Trouble Shooting NeoVim's rplugin not found

NeoVim is awesome. But after its 0.1 release, neovim is not that awesome, after all, in a old vimmer’s eye.

To support XDG configuration, NeoVim changed default config paths. After that patch, neovim search ~/.config/nvim/init.vim rather than our old friend ~/.nvimrc. So I changed my zshrc to alias v to nvim -u ~/.nvimrc. So I can use old configuration without relocating files.

It works fine, except Deoplete always complain about its remote plugin is not registered. When I execute UpdateRemotePlugins as Deoplete’s doc said, a new .-rplugin- file always spawn in my working directory. Without that weird file, Deoplete will never work.

I think this is configuration problem. But how can I figure out what happened? I start to resolving this by guess.

.-rplugin is the critical file on which Deoplete depends. So NeoVim should search for it when booting. I searched for NeoVim’s repository for its usage. Yes, it does appear in neovim’s source, in neovim/runtime/autoload/remote/host.vim.

It reads:

1
2
let s:remote_plugins_manifest = fnamemodify(expand($MYVIMRC, 1), ':h')
\.'/.'.fnamemodify($MYVIMRC, ':t').'-rplugin~'

Hmmm, NeoVim will find .-rplugin in the same directory of $MYVIMRC. But where does $MYVIMRC come from?

Searching neovim’s doc gives me the answer. In :h starting, it writes:

If Vim was started with “-u filename”, the file “filename” is used.
All following initializations until 4. are skipped. $MYVIMRC is not
set.

Oh, so I have to relocate my vimrc file. After that, every thing works :).

JavaScript Error tracking in browsers

Error tracking is one of the awful part of JavaScript. Server side error tracking requires several configuration, which is not hard because server is under developers’ full control, after all. For Android/iOS applications, error tracking is integrated into integrated into platform framework. Unfortunately, error tracking in browser is like survival in wild jungle.

Here are some common pitfalls.

Incompatible API

JavaScript errors in different browsers have different field names, as usual.

One should never bother to care about api incompatibility among browsers. Here is the snippet to normalize those quirks.
Curse the variable ieEvent.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function errorTracker (err) {
var ieEvent
if (typeof err !== 'object') {
ieEvent = {}
err = window.event
ieEvent.message = err.errorMessage
ieEvent.filename = err.errorUrl
ieEvent.lineno = err.errorLine
err = ieEvent
}

return {
message: err.message,
line: err.lineno || err.lineNumber,
file: err.filename || err.fileName
}
}

CORS Header

You will see a lot of Scritp error message in your CDN hosted javascript file. Browsers report this useless error intentially. Revealing error details to web page in different domain is a security issue. It can leak one’s privacy and helps phishing and social engineering. To quote the SO answer

This behavior is intentional, to prevent scripts from leaking information to external domains. For an example of why this is necessary, imagine accidentally visiting evilsite.com, that serves up a page with <script src="yourbank.com/index.html">. (yes, we’re pointing that script tag at html, not JS). This will result in a script error, but the error is interesting because it can tell us if you’re logged in or not. If you’re logged in, the error might be 'Welcome Fred...' is undefined, whereas if you’re not it might be 'Please Login ...' is undefined. Something along those lines.

And in Chromium’s source code, we can see error is sanitized, if the corsStatus does not satisify some condition.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bool ExecutionContext::shouldSanitizeScriptError(const String& sourceURL, AccessControlStatus corsStatus)
{
if (corsStatus == OpaqueResource)
return true;
return !(securityOrigin()->canRequestNoSuborigin(completeURL(sourceURL)) || corsStatus == SharableCrossOrigin);
}

bool ExecutionContext::dispatchErrorEvent(PassRefPtrWillBeRawPtr<ErrorEvent> event, AccessControlStatus corsStatus)
{
EventTarget* target = errorEventTarget();
if (!target)
return false;

RefPtrWillBeRawPtr<ErrorEvent> errorEvent = event;
if (shouldSanitizeScriptError(errorEvent->filename(), corsStatus))
errorEvent = ErrorEvent::createSanitizedError(errorEvent->world());

ASSERT(!m_inDispatchErrorEvent);
m_inDispatchErrorEvent = true;
target->dispatchEvent(errorEvent);
m_inDispatchErrorEvent = false;
return errorEvent->defaultPrevented();
}

To enable SharableCrossOrigin scripts, one can add crossorigin attribute to script tags, and, add a Access-Control-Allow-Origin head in scripts’ server response, just like cross orgin XHR.

Like this.

1
<script src="http://somremotesite.example/script.js" crossorigin></script>

And in your server conf, say, nginx, add something like this

1
2
3
4
5
6
7
8
server {
server_name static.jscdn.com;
# blah blah blah

location ~ \.js {
add_header Access-Control-Allow-Origin "*";
}
}

More Browser Quirks and nasty ISP

Modern browser will protect users’ privacy and respect developers’ CORS setting. But IE may screw both. In some unpatched Internet Exploers, all script errors are accessible in onError handler, regardless of their origins. But some Internet Explorers, patched, just ignore the CORS head and swallow all crossorigin error messages.

To catch errors in certain IEs, developers must manually wrap their code in try {...} catch (e){report(e)} block. Alternatively, one can use build process to wrap function, like this.

Zone should also be a good candidate for error tracking, and does not require build process. Though I have not tried it.

Another issue in error tracking is ISP and browser extensions. onError callbacks will receive all error in the host page. It usually contains many ISP injected script and extension script which trigger false alarm errors. So wrapping code in try ... catch may be a better solution.

UPDATE:

It seems Zone like hijacking method has been used in error tracking product. Like BugSnag. The basic idea is: If code is executed synchronously, then it can be try ... catch ...ed in one single main function. If code is executed asynchronously, then, by wrapping all function that takes callback, one can wrap all callbacks in try ...catch ....

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function wrap(func) {
// Ensure we only wrap the function once.
if (!func._wrapped) {
func._wrapped = function () {
try{
func.apply(this, arguments);
} catch(e) {
console.log(e.message, "from", e.stack);
throw e;
}
}
}
return func._wrapped;
};

The above code will wrap all func in try and catch. So when error occurs, it will be always logged. However, calling wrapper function in every async code usage is impractical. We can invert it! Not wrapping callbacks, but wrapping functions that consume callbacks, say, setTimeout, addEventListener, etc. Once these async code entries have been wrapped, all callbacks are on track.

And, because JavaScript is prototype based language, we can hijack the EventTarget prototype and automate our error tracking code.

1
2
3
4
var addEventListener = window.EventTarget.prototype.addEventListener;
window.EventTarget.prototype.addEventListener = function (event, callback, bubble) {
addEventListener.call(this, event, wrap(callback), bubble);
}

IE9 and friends

Sadly, IE does not give us stack on error. But we can hand-roll our call stack by traversing argument.callee.caller.

1
2
3
4
5
6
7
8
9
10
11
// IE <9
window.onerror = function (message, file, line, column) {
var column = column || (window.event && window.event.errorCharacter);
var stack = [];
var f = arguments.callee.caller;
while (f) {
stack.push(f.name);
f = f.caller;
}
console.log(message, "from", stack);
}

Garbage Collector Issue

Error reporting is usually done by inserting an Image of which the url is the address of logging server comprised of encoded error info in query string.

1
2
var url = 'xxx';
new Image().src = url;

But the Image has no reference to itself, and JS engine’s garbage collector will collect it before the request is sent. So one can assign the Image to a variable to hold its reference, and withdraw the reference in the onload/onerror callback.

1
2
3
4
5
6
7
var win = window;
var n = 'jsFeImage_' + _make_rnd(),
img = win[n] = new Image();
img.onload = img.onerror = function () {
win[n] = null;
};
img.src = url;

The Real Angular2 quick start

Angular2 official page wants you to make some dirty hack to get the fastest hellow world in Angular2. But it immediately requires to correct your first sin in the same 5-min quickstart page. Maybe it is possible for a newcomert to set up Angular2 properly in 5 minutes, but reverting previous dirty hack and then setting the correct thing up are annoying. So here is the REAL Angular2 quickstart that does not piss you off.

DISCLAIMER: Knowledge about npm, TypeScript and SystemJS is recommended.
This quickstart deliberately skips explanation on the config and shell code for real real speed.

Step 1: Create a new folder for our application project.

1
2
3
mkdir angular2-quickstart
cd angular2-quickstart
mkdir -p src/app

Step 2: Install npm packages

First step towards front end project as usual…

1
2
3
npm init -y
npm i angular2@2.0.0-alpha.44 systemjs@0.19.2 --save --save-exact
npm i typescript live-server --save-dev

Version number sucks, but it will be removed after Angular2’s public stable release.
We need to install angular2 and TypeScript as dependency, of course. SystemJS is used to load our app(alternatively one can use webpack or browserify).
Live-server gives us live-reloading in developement.

Step 4: Set up npm script tasks

Let’s define some useful command.
Find and replace the script section in package.json

1
2
3
4
5
6
...
"scripts": {
"tsc": "tsc -p src -w",
"start": "live-server --open=src"
}
...

npm run tsc compiles and watches file changes. npm start runs the live-reload server.

Step 5: Create our first Angular2 component

Add a new file called app.ts in src/app

1
2
3
4
5
6
7
import {Component, bootstrap} from 'angular2/angular2';
@Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})
class AppComponent { }
bootstrap(AppComponent);

Step 6: Create our entrance html

Create an index.html in src foler.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
<head>
<title>Angular 2 QuickStart</title>
<script src="../node_modules/systemjs/dist/system.src.js"></script>
<script src="../node_modules/angular2/bundles/angular2.dev.js"></script>
<script>
System.config({
packages: {
'app': {defaultExtension: 'js'} // defaultExtension makes require `app/app` works without `.js`
// more packages config goes here
// ...
}
});
System.import('app/app');
</script>
</head>
<body>
<my-app>loading...</my-app>
</body>
</html>

Step 7: Compile TypeScript

Create tsconfig.json in the src folder.

1
2
3
4
5
6
7
8
9
10
11
{
"compilerOptions": {
"target": "ES5",
"module": "commonjs",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
}
}

And then, in a new terminal window.

1
npm run tsc

Step 8: Run the app!

And in yet another new terminal window.

1
npm start

You should see My First Angular2 App in your browser.

Hooray!

Now you can make some changes to your source file. tsc compiles file on the fly and live-server automatically refreshes the browser!

Hope you are not pissed off by this quickstart.

P.S. Final structure

1
2
3
4
5
6
7
8
angular2-quickstart
├── node_modules
├── src
│ ├── app
| │ └── app.ts
│ ├── index.html
│ └── tsconfig.json
└── package.json

Updated Ctags for TypeScript

TypeScript has a decent tooling chain. However, sometimes a simple ctags is more handy and efficient.

Here is a updated ctag configuration for TypeScript. It supports more features in TS 1.5+.

1
2
3
4
5
6
7
8
9
10
11
12
13
--langdef=typescript
--langmap=typescript:.ts
--regex-typescript=/^[ \t]*(export)?[ \t]*class[ \t]+([a-zA-Z0-9_]+)/\2/c,classes/
--regex-typescript=/^[ \t]*(export)?[ \t]*abstract class[ \t]+([a-zA-Z0-9_]+)/\2/a,abstract classes/
--regex-typescript=/^[ \t]*(export)?[ \t]*module[ \t]+([a-zA-Z0-9_]+)/\2/n,modules/
--regex-typescript=/^[ \t]*(export)?[ \t]*type[ \t]+([a-zA-Z0-9_]+)/\2/t,types/
--regex-typescript=/^[ \t]*(export)?[ \t]*namespace[ \t]+([a-zA-Z0-9_]+)/\2/n,modules/
--regex-typescript=/^[ \t]*(export)?[ \t]*function[ \t]+([a-zA-Z0-9_]+)/\2/f,functions/
--regex-typescript=/^[ \t]*export[ \t]+var[ \t]+([a-zA-Z0-9_]+)/\1/v,variables/
--regex-typescript=/^[ \t]*var[ \t]+([a-zA-Z0-9_]+)[ \t]*=[ \t]*function[ \t]*\(\)/\1/l,varlambdas/
--regex-typescript=/^[ \t]*(export)?[ \t]*(public|private)[ \t]+(static)?[ \t]*([a-zA-Z0-9_]+)/\4/m,members/
--regex-typescript=/^[ \t]*(export)?[ \t]*interface[ \t]+([a-zA-Z0-9_]+)/\2/i,interfaces/
--regex-typescript=/^[ \t]*(export)?[ \t]*enum[ \t]+([a-zA-Z0-9_]+)/\2/e,enums/

For more vim integration, you can give YATS a look

dark
sans