TypeScript学习笔记

TypeScript学习笔记

TypeScript是JavaScript的超集,任何合法的JS程序都是合法的TypeScript程序前端

TypeScript经过向JavaScript增长可选的静态类型声明来把JavaScript变成强类型程序语言。node

静态类型声明可约束函数、变量、属性等程序实体。jquery

TypeScript语言内部分为三层:git

1. 语言层:实现全部TypeScript语言特性github

2.编译层:执行编译,类型检查,将TypeScript代码转换成JavaScript代码ajax

3.语言服务层:生成信息以帮助编译器和其余工具来提供更好的辅助特性typescript

语言特性

全局安装:npm install -g typescriptnpm

使用WebStrom调试json

1. File - Settings - Languages & Frameworks - TypeScript - TypeScript 路径选取全局安装路径gulp

2. 项目 - New - tscofing.json 新增转换项目

此时能够转换成js代码,而后调试添加node指向js代码便可。

使用命令行调试

tsc demo1.ts 能够将TypeScript转换为JavaScript文件

可选静态类型声明

能够声明一个变量的类型,当没有类型声明的时候,会尝试检查赋值。

var counter;
var counter : number;
var counter : number = 0;

基本类型

boolean:true和false,表示是否正确

number:浮点数

string:文本

array:数组,let list : number[] = [] or let list:Array<number> = []

enum:枚举,enum Color {Red,Blue}; var c : Color = Color.Red

any:任意JS值进行最小静态化检查,var list:any[] = [1,true,'']

void:没有返回值

注:undefined 和 null,不能做为变量类型赋值

联合类型

声明能够存储多种类型值变量

let path : string|string[]

类型守护

能够在运行时使用typeof或者instanceof运算符对类型进行验证

环境声明

容许在TypeScript代码中建立一个不会被编译到JavaScript中的变量。

若是但愿调用一个未被定义的对象上的方法,可使用declare操做符建立一个环境声明。

此时就不会有编译错误

TypeScript,支持do while \ for in \ while  \ for ,四种循环方式

函数经过三种方式声明

//具名函数
function greet(name?:string){}
//匿名函数
const greet = function(name?:string){}
//箭头函数
const greet = (name:string):string=>{}

Bower

包管理工具,只用于管理前端的依赖。所以Bower中的许多包也都针对前端作过优化。

安装:npm install -g bower

建立配置文件:bower init 与 npm init 殊途同归

安装包:bower install jquery --save-dev

全部Bower包都保存在bower_components目录下,也须要添加.gitignore中

tsd

TypeScript包含一个lib.d.ts文件,描述JS内建对象、文档对象(DOM)、浏览器对象(BOM)的API。

扩展名为.d.ts的文件是一种特殊的TypeScript文件,称为类型定义文件或描述文件。

一个类型描述文件一般对包含第三方库API的类型声明,使这些库现存的JS库与TS集成在一块儿。

例如:TypeScript调用jQuery会获得报错。

$.ajax({});  //cannot find name '$'

须要添加jQuery描述文件的引用,例如

///<reference path="node_modules/@types/jquery/index.d.ts" />

tsd就是包含这些引用的开源项目DefinitelyTyped

tsd用来管理TypeScript应用中描述文件的工具,和npm\bower工具同样,也有本身的配置文件tsd.json

安装:npm install tsd -g

建立配置文件:tsd init

安装描述文件:tsd install jquery --save

可是这种方法目前用不了,使用过渡方法:npm install --save @types/jquery

官网:http://definitelytyped.org/

自动化工具

自动化工具能够自动完成开发中的重复任务,例如:编译TS文件,压缩JS文件等

现在最流行的两个自动化工具就是,Grunt 和 Gulp

Grunt中使用文件做为任务的输入和输出,Gulp中是使用流。Grunt插件使用键值对配置,Gulp插件使用代码描述任务。

可读性Gulp高于Grunt.

Gulp

安装:npm install -g gulp

开发依赖:npm install gulp --save-dev

项目根目录建立gulpfile.js,用来配置gulp命令,内容以下:

let gulp = require('gulp');

gulp.task('default',function(){
   console.log("Hello Gulp");
});

注意此时不能使用import引入,不然没法进行测试。

执行gulp命令,会检索当前目录下的gulpfile.js,找到后执行default任务。

代码质量

可使用Gulp插件中的tslint来检查代码质量。

安装:npm install typescript --save-dev

安装:npm install tslint --save-dev

安装:npm install gulp-tslint --save-dev

添加新任务(没起做用不知道为何)

var gulp = require('gulp');
var tslint = require('gulp-tslint');

gulp.task('lint', function () {
    return gulp.src([
        './source/**.ts', './test/**/**.test.ts'
    ]).pipe(tslint())
        .pipe(tslint.report('verbose'));
});
gulp.task('default', ['lint']);

编译TypeScript代码

添加两个编译TypeScript代码的任务,一个编译应用的逻辑代码,另外一个编译单元测试的代码

安装:npm install gulp-typescript --save-dev

建立新的gulp-typescript项目对象

var ts = require('gulp-typescript');
var tsProject = ts.createProject({
    removeComments: true,
    noImplicitAny: true,
    target: 'ES5',
    module: "commonjs",
    declarationFiles: false
});

上面代码将TypeScript编译器做为依赖加载进来,而后建立tsProject对象。

包含TypeScript编译器在编译咱们的代码时,须要带上的参数。如今编辑应用源代码

gulp.task('tsc', function () {
    return gulp.src('./source/**/**.ts')
        .pipe(ts(tsProject))
        .js.pipe(gulp.dest('./source/js'));
});

tsc任务会去查找./source/目录及其子目录下全部的.ts文件,而后将他们做为一个输入流传递给TypeScript编译器

编译器会使用tsProject对象的属性做为编译时的参数,并将编译后的JavaScript代码保存在./source/js目录下

针对不一样地方的编译,例如单元测试编译。代码格式同样

再次修改default任务

gulp.task('default', ['tsc']);

优化TypeScript应用

编译完TypeScript代码后,会为每一个TypeScript文件生成对应的JavaScript文件。

可是他们不能运行在浏览器中,由于在浏览器运行的惟一方法就是添加<script>标签。

或者还有如下两种方案

1. 使用RequireJS这样的库,经过AJAX来按需加载各个文件。这种作法称为异步模块加载。

2. 将TypeScript模块参数配置成CommonJS,而后使用Browerify这样的工具,解析应用的模块和依赖。最后生成一个包含应用里全部模块且高度优化的JS文件。

这里使用CommonJS方法,由于Browserify和Gulp是高度集成的。

安装:npm install browserify vinyl-transform gulp-uglify gulp-sourcemaps

导入这些包,而后对他们进行初始化。建立main.js输入下方代码

var browserify = require('browserify');
var transform = require('vinyl-transform');
var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');

var browserified = transform(function (filename) {
    var b = browserify({entries: filename, debug: true});
    return b.bundle();
});

browserified函数会将普通的Node.js流转换为Gulp流。

实现实际的任务

gulp.task('bundle-js', function () {
    return gulp.src('./main.js')
        .pipe(browserified)
        .pipe(sourcemaps.init({loadMaps: true}))
        .pipe(uglify())
        .pipe(sourcemaps.write('./'))
        .pipe(gulp.dest('./dist/source/js'));
});

main.js 做为入口,Browserify会从这个入口出发,追踪应用里全部任务的模块和依赖,而后生成一个高度优化的JS文件的流。

接着会使用uglify插件来最小化输出,这将会减小应用的加载时间,可是会让BUG难以追踪。

咱们生成一个source map文件来简化追踪BUG的过程。uglify会移出代码中的空行和空格,并缩减变量名的长度。

source map使得咱们在追踪bug时可以将最小化后的代码映射到源文件中对应的地方。

咱们能够轻松的追踪一个最小化后的代码的bug,在Chrome和Firefox的开发者工具里,已经內建了对source map的支持。

目前,代码还不能跑,由于如今里面的任务是并行的。须要改为按顺序执行。

管理Gulp任务的执行顺序

Gulp默认的是一步执行全部任务的,有三种方法让一个任务同步执行

1.传递回调函数

2.返回一个流

3.返回一个promise

//传递回调函数
gulp.task('sync',function(cb){
   setTimeout(function(){
       cb();
   },1000) ;
});

//返回一个流
gulp.task('sync',function(){
   return gulp.src('js/*.js')
       .pipe(concat('script.min.js'))
       .pipe(uglify())
       .pipe(gulp.dest('../dist/js'))
});

也能够经过run-sequence的Gulp插件,能够更好的控制任务执行顺序。

安装:npm install run-sequence --save-dev

var runSequence = require('run-sequence');
gulp.task('default',function(cb){
    runSequence(
        'lint', // lint
        ['tsc'], // 并行执行tsc
        ['bundle-js'], //并行执行bundle-js
        'karma', // test
        'browser-sync', // callback
        cb
    );
})

自动化测试工具

自动化测试工具让咱们可以自动化执行应用里面的单元测试。

经过自动化测试工具,能够自动在多个浏览器内执行应用的测试套件,而不用打开浏览器运行测试。

自动化测试工具为Karma,Karma能够和多个流行的单元测试框架兼容。这里的测试框架为Mocha,Chai(断言库),Sinon(数据模拟框架)

安装测试框架:npm install mocha chai sinon --save-dev

安装Karma做为开发依赖:npm install karma karma-mocha karma-chai karma-sinon karma-coverage karma-phantomjs-launcher gulp-karma --save-dev

var karma = require('gulp-karma');
gulp.task('karma', (cb) => {
    gulp.src('./dist/test/**/**.test.js')
        .pipe(karma({configFile: 'karma.conf.js', action: 'run'}))
        .on('end', cb)
        .on('error', (err) => {
            throw err;
        })
});

获取了test及其子目录下全部.test.js文件,而后把他们连同karma.conf.js文件一同传递给Karma插件。

须要在根目录下建立一个名为karma.conf.js的文件,

module.exports = function (config) {
    config.set({
        basePath: '',
        frameworks: ['mocha', 'chai', 'sinon'],
        browsers: ['PhantomJS'],
        reporters: ['progress', 'coverage'],
        plugins: [
            'karma-coverage',
            'karma-mocha',
            'karma-chai',
            'karma-sinon',
            'karma-phantomjs-launcher'
        ],
        preprocessors: {
            './test/*.test.js': ['coverage']
        },
        port: 9876,
        colors: true,
        autoWatch: false,
        singleRun: false,
        logLevel: config.LOG_INFO
    });
};

配置文件说明了Karma应用根目录、框架、浏览器、插件须要报告的测试执行期间的信息。

PhantomJS是一个无界面Web浏览器,使用它就无须打开一个真正的Web浏览器,而执行单元测试代码。

跨设备测试同步 

在浏览器中运行咱们的应用,须要先安装browser-sync包

安装:npm install -g browser-sync

版本不一致,暂且搁置

脚手架工具

能够自动生成项目的文件结构、构建脚本等。

最流行的就是Yeoman,内置命令yo,同时也是包管理工具,基于模板生成项目

省城项目的模板在Yeoman中共称为生成器。生成器地址:http://yeoman.io/generators/

安装:npm install -g yo

安装生成器:npm install -g angular-typescript-gulp typings

安装完成后结合yo命令一同使用:yo angular-typescript-gulp

接着根据提示填写就能够了。

函数

参数能够设置静态类型声明,返回值也能够设置静态类型声明。好比下方代码,就会编译报错,由于须要返回number

function named(name: string): number {
return "";
}

具名函数和匿名函数,行为并不同。具名函数会优先识别,匿名函数除非表达式被赋值,不然不会被执行。

当咱们须要匿名函数返回自己时,能够进行以下定义,来减小冗余的类型声明。

var greetUnnamed: (name: string) => string = function (name: string): string {
    return "Chenxy"
}

若是不须要返回值能够写成

(name : string) : void;

注意:函数类型和变量类型,必须相同

可选参数

可选参数能够在参数名后方添加?号来防止编译报错

function add(foo?: number) {
    console.log(foo);
}

能够在类型声明后面添加默认参数

function add(foo: number = 1) {
    console.log(foo);
}

rest参数 

TypeScript也能够写rest参数来用一个数组接受多个参数。一样也支持扩展运算符

function add(...foo: number[]) {
    console.log(foo);
}

add(1, 2, 3, 4); //[ 1, 2, 3, 4 ]

函数重载

不一样类型的方法能够经过声明一个函数的全部函数签名,而后将逐个签名一一实现,来达到函数重载的功效。

function foo(age: number): string;
function foo(name: string): string;
function foo(value: (number | string)): string {
    switch (typeof value) {
        case "string":
            return `string`;
        case "number":
            return `number`;
    }
}

console.log(foo(1)); //number
console.log(foo("chenxy")); //string

实现签名必须兼容全部的重载签名。直接执行签名函数会获得一个编译错误。

泛型

可使用T来定义泛型参数

///<reference path="node_modules/@types/jquery/index.d.ts" />
function getEntitytes<T>(name: string, entity: T): void {
    console.log(`name=${name},entity=${entity.get()}`);
}

class User {
    _name: string;
    get = () => this._name;
    constructor(name) {
        this._name = name;
    }
}

getEntitytes<User>("chenxy", new User("chenxy"));

类 

TS中也可使用public或private访问符。

接口

能够定义实现规则,接口的规则就是方法和属性的签名,继承接口的类必须实现他们。

接口有两点不一样:

1. 接口能够扩展其余接口或者类

2. 接口能够定义数据和行为而不仅是行为

继承

使用extends能够继承某个父类,会继承全部方法和属性

class Teacher extends Person

使用super.method() 能够调用父类方法,来实现重写

若是子类有构造,则必需要构造中 super() 来构造父类,不然没法使用。

使用implements能够继承多个接口,必须实现接口中定义的方法或属性签名。

interface IUser{
    get():string;
    set:string;
}

class User implements IUser{
    constructor(name) {
        this._name = name;
    }
    set: string;
    _name: string;
    get = () => this._name;
}

泛型约束

使用extends能够给泛型进行接口约束。

function getEntitytes<T extends IUser>(name: string, entity: T): void {
    console.log(`name=${name},entity=${entity.get()}`);
}

interface IUser{
    get():string;
    set:string;
}

class User implements IUser{
    constructor(name) {
        this._name = name;
    }
    set: string;
    _name: string;
    get = () => this._name;
}

getEntitytes<User>("chenxy", new User("chenxy"));

多重约束 

泛型约束不能进行多重约束,可是可使用组合接口来实现

须要定义组合接口,而且继承接口A\B,而后泛型约束组合接口便可。

interface IA {
    A: string;
}

interface IB {
    B: string;
}

interface IC extends IA, IB {
    A: string;
    B: string;
}

class C implements IC {
    A: string = "CA";
    B: string = "CB";
}

class Person<T extends IC> {
    Entity: T;

    constructor(entity: T) {
        this.Entity = entity;
    }

    Con(): void {
        console.log(`A=${this.Entity.A},B=${this.Entity.B}`)
    }
}

const P = new Person(new C());
P.Con();

建立新泛型 

若是咱们要建立新的泛型(new T()),须要使用下方代码

Copy(): T {
        var type: { new(): T; }
        return new type();
    }

命名空间

直接嵌套在类上面便可。namespace app {}

模块

TypeScript使用模块加载器来进行模块加载。

模块加载器是在模块加载过程当中为咱们提供更好控制能力的工具,能优化加载任务,好比异步加载文件或轻松合并多个模块到单一的高度优化文件中。

不推荐使用<script>,由于浏览器不会异步加载此标签。

使用ES6进行模块加载时,须要使用export关键字来定义外部模块。跟ES6语法一致

class UserModel {

}

export {UserModel}

引入使用 import ,跟ES6语法一致。

应用测试

软件测试术语

断言:一个条件,是必须被测试确认的一段代码的行为是否与指望项目,与要求一致。

测试规范:软件开发人员用来指代测试规范的一个术语,是一个详细的清单,包含了须要测试的场景,如何被测试等。

测试用例:决定一个程序中的功能是否按照原始指望工做的一些条件。断言是一个条件、测试用例是一组条件。

测试套件:许多测试用例的集合。一个测试套件能够包含多个测试用例来覆盖不少的场景。

测试监控:某些测试框架提供的功能,容许咱们包括一个函数,并记录它的使用状况。

替身:测试执行时被传入但并无实际用到的对象。

测试桩:容许包括一个方法而后观察它的使用状况。

模拟:为测试提供输入来决定测试是否可以经过。

测试覆盖率:指程序中有多大比例的代码经过自动化测试被测试到。

必备工具

npm init

添加Gulp运行必要任务:npm install gulp -g

添加Karma自动执行测试:npm install --save-dev karma

Karma插件更容易建立测试覆盖率报告:npm install --save-dev karma-coverage

Istanbul指出哪一行代码在自动化测试中被测试到的工具。生成覆盖率报告

Mocha测试框架库,方便的建立测试套件、测试用例和测试规范:npm install --save-dev mocha karma-mocha

Chai支持测试驱动开发和行为驱动开发的断言库:npm install --save-dev chai karma-chai

Sion.JS一个独立的框架提供API能够独立测试一个组件:npm install --save-dev sinon karma-sinon

类型定义防止ts用第三方库编译错误:

npm install --save-dev @types/mocha

npm install --save-dev @types/chai

npm install --save-dev @types/sinon

npm install --save-dev @types/jquery

PhantomJS无显示页面的浏览器:npm install --save-dev phantomjs / npm install --save-dev karma-phantomjs-launcher

Selenium测试运行期,只运行端对端(E2E)测试的特定测试。

Nightwatch.JS自动化测试框架,使用Selenium网络驱动API,完整的浏览器(E2E)测试解决方案。

npm install --save-dev gulp-nightwatch

npm install --save-dev selenium-standalone -g

selenium-standalone install (须要JAVA环境)

测试计划和方法

测试方面主要两种风格或方法可选:测试驱动开发(TDD)和行为驱动开发(BDD)

测试驱动开发

鼓励开发者在写程序代码以前写测试。使用TDD编码包含如下基本步骤:

1. 编写不经过的测试

2. 运行这个测试,保证不经过

3. 编写应用代码,让测试经过。

4.运行这个测试,保证经过

5.运行全部其余测试,保证没有被破坏

6.重复以上步骤

TDD是被推荐的,由于他会极大程度的帮助你和你的团队增长程序的测试覆盖率。

行为驱动测试

描述并阐明测试应该关注程序的需求而非测试的需求。鼓励开发者少思考测试这件事而更多的思考整个程序。

测试类型

单元测试:经过设置测试模拟和依赖注入来尽量的让测试独立。

部分集成测试和总体集成测试:用来测试一组组件或整个程序。

回归测试:用来确认程序错误是否被修复。

性能/加载测试:用来确认程序是否达到性能预期。

端对端(E2E)测试:用来测试一组组件或整个程序。

验收测试(UAT):验收是否符合用户的全部需求

基础结构

根目录建立两个文件夹source、test

Gulp构建

npm install --save-dev gulp

npm install --save-dev browserify

npm install --save-dev vinyl-source-stream

npm install --save-dev vinyl-buffer

npm install --save-dev gulp-run

npm install --save-dev gulp-nightwatch

npm install --save-dev tslint

npm install --save-dev typescript

npm install --save-dev gulp-tslint

npm install --save-dev gulp-typescript

npm install --save-dev browser-sync

npm install --save-dev karma

npm install --save-dev gulp-uglify

npm install --save-dev gulp-docco

npm install --save-dev run-sequence

npm install --save-dev gulp-header

未完待续 

装饰器

只有TypeScript1.5或更高版本才可使用。

安装:

npm install --save-dev gulp gulp-typescript typescript

npm install --save reflect-metadata

建立gulpfile.js文件,添加编译代码的任务。

var gulp = require('gulp');
var ts = require('gulp-typescript');
var typescript = require('typescript');

var tsProject = ts.createProject({
    removeComments: false,
    noImplicitAny: false,
    target: 'ES5',
    module: "commonjs",
    declarationFiles: false,
    emitDecoratorMetadata: true,
    typescript: typescript
});

gulp.task("build-source", function () {
    return gulp.src(__dirname + "/file.ts")
        .pipe(tsc(tsProject))
        .js
        .pipe(gulp.dest(__dirname + "/"));
})

注解:一种为类声明添加元数据的方法,而后元数据就能够被诸如依赖注入容器这样的工具所使用()。

装饰器:是ECMAScript7标准的特性,用来在代码设计时注释和修改类和类的属性。

只须要关注装饰器便可,由于他是真正的语言标准。

使用下面的类,来讲明如何使用装饰器。

class Person {
    public Name: string;
    public Surname: string;

    constructor(private name: string, private surname: string) {
        this.Name = name;
        this.Surname = surname
    }

    public saySomething(something: string): string {
        return `${this.Name} ${this.Surname} says: ${something}`;
    }
}

装饰器一共四中,分别用来装饰:类、属性、方法、参数

类装饰器

类装饰器用来修改类的构造函数,若是返回undefined那么类仍然使用原来的构造函数。

若是装饰器有返回值,那么返回值会被用来覆盖类原来的构造函数。

建立一个logClass的类装饰器。

function logClass(target:any){}

使用装饰器须要在类上面 @logClass

若是已经声明并使用一个装饰器,通过TypeScript编译后的代码中会有一个__decorate的函数。

@logClass
class Person {
    public Name: string;
    public Surname: string;

    constructor(private name: string, private surname: string) {
        this.Name = name;
        this.Surname = surname
        console.log('Person');
    }

    public saySomething(something: string): string {
        return `${this.Name} ${this.Surname} says: ${something}`;
    }
}

function logClass(target: any) {
    var orginal = target;

    function construct(constructor, args) {
        var c: any = () => constructor.apply(this, args);
        c.prototype = constructor.prototype;
        return new c();
    }

    var f: any = (...args) => {
        console.log('logClass');
        return construct(orginal, args);
    }
    f.prototype = orginal.prototype;
    return f;
}

new Person("chen", "xy"); //logClass Person

类装饰器接受一个参数,即类的构造函数。意味着target参数就是Person类的构造函数。

装饰器先复制类的原有构造,而后定义一个名为construct的工具函数来生成类的实例。

装饰器用来为元素添加一些额外的逻辑或元数据,咱们想要拓展一个函数的功能时。

须要往原函数上包一个新的函数,有额外逻辑,且能执行原函数的方法。

方法装饰器

与类装饰器类似,用来覆盖类的方法。

若是返回的不是undefined,那么返回值将会覆盖方法的属性描述对象。

被调用时,带有如下参数:

包含了被装饰方法的类的原型,即Person.prototype

被装饰方法的名字,即saySomething

被装饰方法的属性描述对象,即Obejct

class Person {
    public Name: string;
    public Surname: string;

    constructor(public name: string, private surname: string) {
        this.Name = name;
        this.Surname = surname
        console.log('Person');
    }

    @logMethod
    public saySomething(something: string): string {
        return `${this.Name} ${this.Surname} says: ${something}`;
    }
}

function logMethod(target:any,key:string,descriptor:any){
    var orginalMethod = descriptor.value;
    descriptor.value = function(...args:any[]){
        var a = args.map(a=>JSON.stringify(a)).join();
        var result = orginalMethod.apply(this,args);
        var r = JSON.stringify(result);
        console.log('logMethod');
        return result;
    }
    return descriptor;
}

const P = new Person("chen", "xy");
console.log(P.saySomething("c")); //logClass Person

建立被装饰元素的副本。

建立一个新函数来替代被装饰的函数,新函数除了调用原函数以外,还包含一些额外逻辑。

属性装饰器

一个属性装饰器没有返回值且没有第三个参数

class Person {
    @logProperty
    public Name: string;
    public Surname: string;

    constructor(public name: string, private surname: string) {
        this.Name = name;
        this.Surname = surname
        console.log('Person');
    }

    public saySomething(something: string): string {
        return `${this.Name} ${this.Surname} says: ${something}`;
    }
}

function logProperty(target: any, key: string) {
    var _val = this[key];
    var getter = function () {
        console.log(`Get:${key} => ${_val}`);
        return _val;
    }
    var setter = function (newValue: any) {
        console.log(`Set:${key} => ${_val}`);
        _val = newValue;
    }
    if (delete this[key]) {
        Object.defineProperty(target, key, {
            get: getter,
            set: setter,
            enumerable: true,
            configurable: true
        });
    }
}

const P = new Person("chen", "xy");
console.log(P.saySomething("c")); //logClass Person

建立原属性副本,声明两个函数:getter\settet

手动删除原属性,并使用Object.defineProperty来建立新属性 

参数装饰器

接受三个参数

包含被装饰参数的方法的对象

方法的名字

参数在参数列表中的索引

参数属性没有返回值,意味着不能覆盖包含修饰参数的方法。

class Person {
    public Name: string;
    public Surname: string;

    constructor(public name: string, private surname: string) {
        this.Name = name;
        this.Surname = surname
        console.log('Person');
    }

    public saySomething(@addMetadata something: string): string {
        return `${this.Name} ${this.Surname} says: ${something}`;
    }
}

function addMetadata(target: any, key: string, index: number) {
    var metadataKey = `Log_${key}`;
    if (Array.isArray(target[metadataKey])) {
        target[metadataKey].push(index);
    } else {
        target[metadataKey] = [index];
    }
}

const P = new Person("chen", "xy");
console.log(P.saySomething("c")); //logClass Person

 #

相关文章
相关标签/搜索