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

 #

相關文章
相關標籤/搜索