TypeScript 注解(上)

引言

上次学习了一下typecirpt-ioc项目,一个优秀的IOC容器,那个项目中用到了TypeScript注解,反正比我写的容器高级多了。是时候学习一下TypeScript注解了。

探究

测试环境选择

WebStorm环境下建立一个示例项目,试了一下,报错。

TypeScript 注解(上)

Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option to remove this warning.

对装饰器的实验性支持是一个在未来版本中可能会发生变化的特性,请设置xxx选项以移除该警告。

注解发展史

编译器所说的装饰器,其实就是我们所说的注解。看这个提示,应该是目前还不支持注解,还是在试验阶段。咦?那为什么Angular启用了注解呢?

后来查到了这张图:

TypeScript 注解(上)

主流浏览器全面支持ES5,所以我们无论用AngularJS的原生JavaScript开发也好,用AngularReactTypeScript开发也好,最后到浏览器执行的代码都是ES5脚本。

我们应该认识下面三个圈,ES5ES6涵盖了ES5并扩充了类与模块,TypeScript又是ES6的超集。

最外围的AtScriptGoogle AtScript团队提出,对TypeScript又进行了扩充,支持注解与introspection(这个不知道是啥,百度翻译是“自我反省”,水平不够不敢随便翻译)。

Google AtScript团队已经将注解作为ES7的提案。但是经过测试,ES7环境下还不支持注解,可能是草案没有通过?

TypeScript 注解(上)

后来,Google AtScriptMicrosoft合作:

We’re excited to announce that we have converged the TypeScript and AtScript languages, and that Angular 2, the next version of the popular JavaScript library for building web sites and web apps, will be developed with TypeScript.

我们很高兴:我们已经融合了TypeScriptAtScript,并且Angular 2将采用TypeScript来开发。

这就是我们的Angular。同样是TypeScript,我们可以像Spring中一样去加注解,而不需要像React一样去继承。

class ShoppingList extends React.Component {
    render() {
        return (
            <div className="shopping-list">
                <h1>Shopping List for {this.props.name}</h1>
                <ul>
                    <li>Instagram</li>
                    <li>WhatsApp</li>
                    <li>Oculus</li>
                </ul>
            </div>
        );
    }
}

自定义注解

新建tsconfig.json文件,将compilerOptions中的experimentalDecorators设置为true,以忽略警告。

TypeScript 注解(上)

注解(装饰器)是一类特殊类型的声明,可以用在类、方法、构造器、属性和参数上。

其实本质上,定义一个注解,就是定义一个TypeScript方法,只是该方法必须符合官方的规范。

注解工厂

定义两个方法helloworld方法,两个方法分别返回符合规范的函数闭包,参数targetpropertyKeydescriptor。经测试,这三个参数中targetpropertyKey是必须的,没有的话编译过不去,descriptor可以省略。

function hello() {
    console.log("hello(): 加载.");
    return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("hello(): 执行.");
    }
}

function world() {
    console.log("world(): 加载.");
    return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("world(): 执行.");
    }
}

class Main {

    @hello()
    @world()
    method() {
        console.log('method(): 执行.');
    }
}

编译、执行:

TypeScript 注解(上)

注解方法在编译期间执行。注解方法加载从上到下,闭包功能执行从下到上。

TypeScript 注解(上)

打印三个参数的结果,具体含义在下面的方法注解一栏中讲解。

类注解

类注解应用于类的构造函数,可以使用它去观察、修改或替换类的定义。类注解不能在声明文件中被使用,或其他ambient context中使用。

class Person {

    message: string;

    constructor(message: string) {
        this.message = message;
    }

    greet() {
        console.log(`Hello ${this.message} !`);
    }
}

const person = new Person('World');
person.greet();

TypeScript 注解(上)

使用类注解,我们可以覆盖原来的构造函数,所以依赖注入可能就是用这种方式实现的。

function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
    return class extends constructor {
        message = 'Decorator';
    }
}

@classDecorator
class Person {

    message: string;

    constructor(message: string) {
        this.message = message;
    }

    greet() {
        console.log(`Hello ${this.message} !`);
    }
}

const person = new Person('World');
person.greet();

TypeScript 注解(上)

覆盖掉了原来的构造函数。

方法注解

方法注解应用于方法的属性描述器,也可以观察、修改替换方法定义。方法注解不能在声明文件中被使用,或者是方法重载,或其他ambient context中使用。

class Person {

    message: string;

    constructor(message: string) {
        this.message = message;
    }

    greet() {
        console.log(`Hello ${this.message} !`);
    }
}

const person = new Person('World');
for (const property in person) {
    console.log(property);
}

TypeScript 注解(上)

for in person对象,遍历出了messagegreet

function enumerable(value: boolean) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.enumerable = value;
    };
}

class Person {

    message: string;

    constructor(message: string) {
        this.message = message;
    }

    @enumerable(false)
    greet() {
        console.log(`Hello ${this.message} !`);
    }
}

const person = new Person('World');
for (const property in person) {
    console.log(property);
}

添加方法注解,@enumerable,将该方法设置为不可枚举。

TypeScript 注解(上)

遍历时就没有greet了,恕我直言,我觉得这个属性描述器好像没什么用。

致歉

身体抱恙,头脑混乱,若有错误之处欢迎指出,还有一个利用反射实现的属性装饰器,以后再与大家详述。

总结

工程要的是经验,框架要的是功底。你喜欢哪一个?

相关推荐