前端必備 TypeScript 基礎知識全總結

2020-08-12 16:51:39

TypeScript 基礎

一、TypeScript中的數據型別

typescript中爲了使編寫的程式碼更規範,更有利於維護,增加了型別校驗,在typescript中主要給我們提供了以下數據型別

ts 程式碼 必須指定型別

  • 布爾型別(boolean)
  • 數位型別(number)
  • 字串型別(string)
  • 陣列型別(array)
  • 元組型別(tuple)
  • 列舉型別(enum)
  • 任意型別(any)
  • null 和 undefined
  • void型別
  • never型別
1、布爾型別(boolean)

es5的正確寫法, ts中是錯誤寫法

var flag = true;
flag = 456;

typescript中爲了使編寫的程式碼更規範,更有利於維護,增加了型別校驗

var flag: boolean = true;
// flag = 123;  // 錯誤
flag = false;  // 正確
console.log(flag);
2、數位型別(number)
var num: number = 123;
num = 456;
console.log(num);  // 正確
num = 'str';    // 錯誤
3、字串型別(string)
var str:string = 'this is ts';
str = 'haha';  // 正確
str = true;  // 錯誤
4、陣列型別(array)

ts中定義陣列有兩種方式 :

  1. var arr: number[] = [11, 22, 33]
  2. var arr: Array<number> = [11, 22, 33]
// var arr=['1','2'];  // es5定義陣列
var arr: number[] = [11, 22, 33];
console.log(arr);
var arr: Array<number>=[11, 22, 33];
console.log(arr)
5、 元組型別(tuple)

元組型別是屬於陣列的一種

// 正常陣列
var arr: Array<number> = [11, 22, 33];
console.log(arr)
// 元祖型別
let arr: [number, string] = [123, 'this is ts'];
console.log(arr);
6、列舉型別(enum)

隨着計算機的不斷普及,程式不僅只用於數值計算,還更廣泛地用於處理非數值的數據。
例如:性別、月份、星期幾、顏色、單位名、學歷 學曆、職業等,都不是數值數據。
在其它程式設計語言中,一般用一個數值來代表某一狀態,這種處理方法不直觀,易讀性差。
如果能在程式中用自然語言中有相應含義的單詞來代表某一狀態,則程式就很容易閱讀和理解。
也就是說,事先考慮到某一變數可能取的值,儘量用自然語言中含義清楚的單詞來表示它的每一個值,
這種方法稱爲列舉方法,用這種方法定義的型別稱列舉型別。

   enum 列舉名{ 
         識別符號[=整型常數], 
         識別符號[=整型常數], 
         ... 
         識別符號[=整型常數], 
     } ;     
enum Flag { success = 1, error = 2 };
let s: Flag = Flag.success;
console.log(s);

enum Color { blue , red, 'orange' };
var c: Color = Color.red;
 console.log(c);   // 1  如果識別符號沒有賦值 它的值就是下標
enum Color { blue, red = 3, 'orange' };
var c: Color = Color.red;
console.log(c);   // 3
var c: Color = Color.orange;
console.log(c);   // 4
enum Err { 'undefined' = -1, 'null' = -2, 'success' = 1 };
var e: Err = Err.success;
console.log(e);
7、任意型別(any)
var num: any = 123;
num = 'str';
num = true;
console.log(num)
// 任意型別的用處
var oBox: any = document.getElementById('box');
oBox.style.color = 'red';
8、null 和 undefined

nullundefined 其他(never型別)數據型別的子型別

var num: number;
console.log(num)  // 輸出:undefined   報錯
var num: undefined;
console.log(num)  // 輸出:undefined  //正確
var num: number | undefined;
num = 123;
console.log(num);
// 定義沒有賦值就是undefined
var num: number | undefined;
console.log(num);
var num: null;
num = null;
//一個元素可能是 number型別 可能是null 可能是undefined
var num: number | null | undefined;
num = 1234;
console.log(num)
9、void型別

typescript 中的 void 表示沒有任何型別,一般用於定義方法的時候方法沒有返回值。
表示方法 沒有返回任何型別

// es5的定義方法
function run() {
	console.log('run')
}
run();
// 正確寫法
function run(): void {
    console.log('run')
}
run();
// 錯誤寫法
function run(): undefined {
   console.log('run')
}
run();
// 正確寫法
function run(): number {
	return 123;
}
run();
9、never型別

是其他型別 (包括 nullundefined)的子型別,代表從不會出現的值

// 這意味着宣告never的變數只能被never型別所賦值。
var a: undefined;
a = undefined;

var b: null;
b = null;
var a: never;
// a = 123; // 錯誤的寫法
a = (() => {
     throw new Error('錯誤');
})()

二、TypeScript中的函數

  • 函數的定義
  • 可選參數
  • 預設參數
  • 剩餘參數
  • 函數過載
  • 箭頭函數 es6
1、函數的定義

es5定義函數的方法

// 函數宣告法
function run() {
    return 'run';
}
// 函數表達式
var run2 = function() {
    return 'run2';
}

ts中定義函數的方法

//函數宣告法
function run(): string {
     return 'run';
}

//錯誤寫法
function run(): string {
    return 123;
}

// 函數表達式
var fun2 = function(): number {
     return 123;
}
fun2(); /* 呼叫方法 */

ts中定義方法傳參

function getInfo(name: string, age: number): string {
     return `${name} --- ${age}`;
}
getInfo('zhangsan', 20);
var getInfo = function(name: string, age: number): string {
     return `${name} --- ${age}`;
}
getInfo('zhangsan', 40);

沒有返回值的方法

function run(): void {
     console.log('run')
}
run();
2、方法可選參數

es5裏面方法的實參和行參可以不一樣,但是ts中必須一樣,如果不一樣就需要設定可選參數

注意: 可選參數必須設定到參數的最後面

// 正確寫法
function getInfo(name: string, age?: number): string {
   if(age) {
       return `${name} --- ${age}`;
   }else{
       return `${name} ---年齡保密`;
   }
}
getInfo('zhangsan');
getInfo('zhangsan',123);

// 錯誤寫法
function getInfo(name?: string, age: number): string {
    if(age) {
        return `${name} --- ${age}`;
    }else{
        return `${name} ---年齡保密`;
    }
 }

getInfo('zhangsan')
3、預設參數 可選參數

es5 裏面沒法設定預設參數,es6 和 ts 中都可以設定預設參數

function getInfo(name: string, age: number = 20): string {
   if(age) {
       return `${name} --- ${age}`;
   }else{
       return `${name} ---年齡保密`;
   }
}
getInfo('張三');
getInfo('張三',30);
4、剩餘參數
function sum(a: number, b: number, c: number, d: number): number {
	return a + b + c + d;
}
sum(1,2,3,4);

三點運算子 接受新參傳過來的值

function sum(...result: number[]): number {
	var sum = 0;
	for(var i = 0;i < result.length; i++){
	    sum += result[i];  
	}
	return sum;
}
sum(1, 2, 3, 4, 5, 6);
function sum(a: number, b: number, ...result: number[]): number {
   var sum = a + b;
   for(var i = 0;i< result.length; i++){
       sum += result[i];  
   }
   return sum;
}
sum(1, 2, 3, 4, 5, 6);
5、ts函數過載

java中方法的過載:過載指的是兩個或者兩個以上同名函數,但它們的參數不一樣,這時會出現函數過載的情況。

typescript中的過載:通過爲同一個函數提供多個函數型別定義來試下多種功能的目的。
ts爲了相容es5 以及 es6 過載的寫法和java中有區別。
es5中出現同名方法,下面 下麪的會替換上面的方法

// es5方法覆蓋
function css(config) {
}

function css(config, value) {
}

ts中的過載

function getInfo(name: string): string;
function getInfo(age: number): string;
function getInfo(str: any): any {
	if(typeof str === 'string') {
         return '我叫:' + str;
     }else{
        return '我的年齡是' + str;
     }
 }
getInfo('張三');    // 正確
getInfo(20);		// 正確
getInfo(true);		// 錯誤寫法
function getInfo(name: string): string;
function getInfo(name: string, age: number): string;
function getInfo(name: any,age?: any): any {
	if(age) {
	 	return '我叫:'+ name + '我的年齡是' + age;
	} else {
	 	return '我叫:' + name;
	}
}
getInfo('zhangsan');  // 正確 
getInfo(123);  // 錯誤
getInfo('zhangsan', 20); // 
6、箭頭函數 es6

this 指向的問題 箭頭函數裏面的this指向上下文

setTimeout(function() {
    console.log('run')
}, 1000)

setTimeout(() => {
    console.log('run')
}, 1000)

三、TypeScript中的類

  • 類的定義
  • 繼承
  • 類裏面的修飾符
  • 靜態屬性 靜態方法
  • 抽象類 繼承 多型
1、ts中類的定義
// es5
function Person(name){
	this.name = name;
	this.run = function() {
		console.log(this.name)
	}
}
var p = new Person('張三');
p.run()
// ts中定義類
class Person{
    name: string;   //屬性  前面省略了public關鍵詞
    constructor(n: string){  //建構函式   範例化類的時候觸發的方法
        this.name = n;
    }
    run(): void {
        alert(this.name);
    }
}
var p = new Person('張三');
p.run()
class Person{
    name: string; 
    constructor(name: string) {  //建構函式   範例化類的時候觸發的方法
        this.name = name;
    }
    getName(): string{
        return this.name;
    }
    setName(name: string): void {
        this.name = name;
    }
}
var p = new Person('張三');
p.getName();
p.setName('李四');
p.getName();
2、ts中實現繼承 extends、 super
class Person{
    name: string;
    constructor(name: string){
        this.name = name;
    }
    run(): string {
        return `${this.name}在運動`
    }
}
var p=new Person('王五');
p.run();

class Web extends Person{
    constructor(name:string){
       super(name);  /*初始化父類別的建構函式*/
    }
}
var w=new Web('李四');
w.run();

ts中繼承的探討 父類別的方法和子類的方法一致

class Person{
    name: string;
    constructor(name: string){
        this.name = name;
    }
    run(): string {
        return `${this.name}在運動`
    }
}
var p = new Person('王五');
p.run()


class Web extends Person{
    constructor(name: string){
        super(name);  /*初始化父類別的建構函式*/
    }
    run(): string {
        return `${this.name}在運動-子類`
    }
    work() {
        alert(`${this.name}在工作`)
    }
}
var w=new Web('李四');
w.work();
w.run();
3、類裏面的修飾符

typescript裏面定義屬性的時候給我們提供了 三種修飾符:

public :公有 在當前類裏面、 子類 、類外面都可以存取
protected:保護型別 在當前類裏面、子類裏面可以存取 ,在類外部沒法存取
private :私有 在當前類裏面可以存取,子類、類外部都沒法存取
屬性如果不加修飾符 預設就是 公有 (public)

public

class Person{
    public name:string;  /*公有屬性*/
    constructor(name:string){
        this.name=name;
    }
    run():string{
        return `${this.name}在運動`
    }
}
var p=new Person('王五');
p.run()


class Web extends Person{
    constructor(name:s tring) {
        super(name);  /*初始化父類別的建構函式*/
    }
    run(): string {
        return `${this.name}在運動-子類`
    }
    work() {
        alert(`${this.name}在工作`)
    }
}
var w = new Web('李四');
w.work();

類外部存取公有屬性

  class Person{
    public name: string;  /*公有屬性*/
    constructor(name: string) {
        this.name = name;
    }
    run(): string {
        return `${this.name}在運動`
    }
}
var  p = new Person('哈哈哈');
p.name;

protected

class Person{
    protected name: string;  /*公有屬性*/
    constructor(name: string) {
        this.name=name;
    }
    run():string{
        return `${this.name}在運動`
    }
}
var p=new Person('王五');
p.run()

class Web extends Person{
    constructor(name: string) {
        super(name);  /*初始化父類別的建構函式*/
    }                  
    work(){
        alert(`${this.name}在工作`)
    }
}
var w = new Web('李四11');
w.work();
w.run();

類外外部沒法存取保護型別的屬性

class Person{
    protected name: string;  /* 保護型別 */
    constructor(name: string) {
        this.name = name;
    }
    run(): string {
        return `${this.name}在運動`
    }
}

var  p = new Person('哈哈哈');
alert(p.name);

private

class Person{
    private name: string;  /*私有*/
    constructor(name: string) {
        this.name = name;
    }
    run(): string {
        return `${this.name}在運動`
    }
}

class Web extends Person{
    constructor(name: string){
        super(name)
    }
    work(){
        console.log(`${this.name}在工作`)
    }
}            
class Person{
    private name: string;  /*私有*/
    constructor(name: string){
        this.name = name;
    }
    run(): string {
        return `${this.name}在運動`
    }
}

var p = new Person('哈哈哈');
p.run();
4、靜態屬性 靜態方法
function Person() {
    this.run1 = function() {
    }
}
Person.name = '哈哈哈';

Person.run2 = function() { //  靜態方法
}
var p = new Person();
Person.run2(); // 靜態方法的呼叫

jquery 舉例

function $(element) {
    return new Base(element)
}
$.get=function() {
}

function Base(element) {
    this.element = `獲取dom節點`;
    this.css=function(arr, value){
        this.element.style.arr = value;
    }
}

$('#box').css('color', 'red')
$.get('url', function() {})

靜態屬性

class Per{
    public name: string;
    public age: number = 20;
    //靜態屬性
    static sex = "男";
    constructor(name: string) {
        this.name = name;
    }
    run() {  /*實體方法*/
        alert(`${this.name}在運動`)
    }
    work() {
        alert(`${this.name}在工作`)
    }
    static print() {  /*靜態方法  裏面沒法直接呼叫類裏面的屬性*/
        alert('print方法'+Per.sex);
    }
}

var p=new Per('張三');
p.run();
Per.print();
Per.sex;
5、多型、抽象類、繼承

多型: 父類別定義一個方法不去實現,讓繼承它的子類去實現 每一個子類有不同的表現

//多型屬於繼承
class Animal {
    name:string;
    constructor(name: string) {
        this.name = name;
    }
    eat(){   // 具體吃什麼  不知道   ,  具體吃什麼?繼承它的子類去實現 ,每一個子類的表現不一樣
        console.log('吃的方法')
    }
}

class Dog extends Animal{
    constructor(name: string){
        super(name)
    }
    eat() {
        return this.name + '吃糧食'
    }
}


class Cat extends Animal{
    constructor(name: string){
        super(name)
    }
    eat() {
        return this.name + '吃老鼠'
    }
}

typescript中的抽象類:它是提供其他類繼承的基礎類別,不能直接被範例化。
abstract關鍵字定義抽象類和抽象方法,抽象類中的抽象方法不包含具體實現並且必須在派生類中實現
abstract抽象方法只能放在抽象類裏面

抽象類和抽象方法用來定義標準 。 標準:Animal 這個類要求它的子類必須包含eat方法

abstract class Animal{
    public name: string;
    constructor(name: string) {
        this.name = name;
    }
    abstract eat(): any;  // 抽象方法不包含具體實現並且必須在派生類中實現。
    run(){
        console.log('其他方法可以不實現')
    }
}

// var a = new Animal() /*錯誤的寫法, 不能範例化*/
class Dog extends Animal{
    // 抽象類的子類必須實現抽象類裏面的抽象方法
    constructor(name: any){
        super(name)
    }
    eat(){
        console.log(this.name + '吃糧食')
    }
}
var d = new Dog('小花花');
d.eat();

class Cat extends Animal{
    //抽象類的子類必須實現抽象類裏面的抽象方法
    constructor(name:any){
        super(name)
    }
    run(){
    }
    eat(){
        console.log(this.name+'吃老鼠')
    }
}

var c = new Cat('小花貓');
c.eat();

四、typeScript中的介面

  • 屬性類介面
  • 函數型別介面
  • 可索引介面
  • 類型別介面
  • 介面擴充套件

介面的作用:在物件導向的程式設計中,介面是一種規範的定義,它定義了行爲和動作的規範,在程式設計裏面,介面起到一種限制和規範的作用。介面定義了某一批類所需要遵守的規範,介面不關心這些類的內部狀態數據,也不關心這些類裡方法的實現細節,它只規定這批類裡必須提供某些方法,提供這些方法的類就可以滿足實際需要。 typescrip中的介面類似於java,同時還增加了更靈活的介面型別,包括屬性、函數、可索引和類等。

1、屬性介面

屬性介面: 對json的約束

ts中定義方法

function printLabel(): void {
    console.log('printLabel');
}
printLabel();

ts 中定義方法傳入參數

function printLabel(label: string): void {
   console.log('printLabel');
}
printLabel('hahah');

ts 中自定義方法傳入參數, 對json進行約束

function printLabel(labelInfo: { label: string }): void {
    console.log('printLabel');
}
printLabel('hahah'); //錯誤寫法
printLabel({ name: '張三' });  //錯誤的寫法
printLabel({ label: '張三' });  //正確的寫法

介面:行爲和動作的規範,對批次方法進行約束

對批次方法傳入參數進行約束。

// 就是傳入物件的約束    屬性介面
interface FullName{
    firstName:string;   //注意;結束
    secondName:string;
}

function printName(name: FullName) {
    // 必須傳入物件  firstName  secondName
    console.log(name.firstName + '--' + name.secondName);
}
// printName('1213');  //錯誤
var obj = {   /*傳入的參數必須包含 firstName  secondName*/
    age: 20,
    firstName: '張',
    secondName: '三'
};
printName(obj)
interface FullName{
    firstName:string;   //注意;結束
    secondName:string;
}

function printName(name: FullName) {
    // 必須傳入物件  firstName  secondName
    console.log(name.firstName + '--' + name.secondName);
}

function printInfo(info: FullName) {
    // 必須傳入物件  firstName  secondName
    console.log(info.firstName + info.secondName);
}

var obj = {   /*傳入的參數必須包含 firstName  secondName*/
    age: 20,
    firstName: '張',
    secondName: '三'
};
printName(obj);
printInfo({
    firstName: '李',
    secondName: '四'
})

可選屬性

interface FullName{
    firstName: string;
    secondName: string;
}

function getName(name: FullName) {
    console.log(name)
}
// 參數的順序可以不一樣
getName({        
    secondName: 'secondName',
    firstName: 'firstName'
})
interface FullName{
    firstName: string;
    secondName?: string;
}

function getName(name: FullName) {
    console.log(name)
}  
getName({               
    firstName:'firstName'
})

簡單封裝舉例, 原生js封裝的ajax

interface Config{
    type: string;
    url: string;
    data?: string;
    dataType: string;
}

// 簡單封裝舉例, 原生js封裝的ajax 
function ajax(config: Config) {
   var xhr = new XMLHttpRequest();
   xhr.open(config.type, config.url, true);
   xhr.send(config.data);
   xhr.onreadystatechange = function() {
        if(xhr.readyState == 4 && xhr.status == 200) {
            console.log('chengong');
            if(config.dataType == 'json') {
                console.log(JSON.parse(xhr.responseText));
            } else {
                console.log(xhr.responseText)
            }
        }
   }
}
ajax({
    type: 'get',
    data: 'name=zhangsan',
    url: 'http://baidu.com/api/productlist', //api
    dataType: 'json'
})

函數型別介面: 對方法傳入的參數 以及返回值進行約束 批次約束
加密的函數型別介面

interface encrypt{
    (key: string, value: string): string;
}
var md5: encrypt = function(key: string, value :string): string{
        //模擬操作
        return key + value;
}
console.log(md5('name', 'zhangsan'));
var sha1: encrypt = function(key: string, value: string): string {
    //模擬操作
    return key + '----' + value;
}
console.log(sha1('name', 'lisi'));

可索引介面:陣列、物件的約束 (不常用)

// ts定義陣列的方式
var arr: number[] = [ 2342, 235325 ]
var arr1: Array<string> = ['111', '222']
//可索引介面 對陣列的約束
interface UserArr{
    [index: number]: string
}
// var arr: UserArr=['aaa', 'bbb'];
// console.log(arr[0]);
var arr: UserArr = [123, 'bbb'];  /*錯誤*/
console.log(arr[0]);

可索引介面 對物件的約束

interface UserObj{
    [index:string]:string
}
var arr:UserObj={name:'張三'};

類型別介面:對類的約束 和 抽象類抽象有點相似

interface Animal{
    name: string;
    eat(str: string): void;
}

class Dog implements Animal{
    name: string;
    constructor(name: string){
        this.name = name;
    }
    eat() {
        console.log(this.name + '吃糧食')
    }
}

var d = new Dog('小黑');
d.eat();

class Cat implements Animal{
    name: string;
    constructor(name: string){
        this.name = name;
    }
    eat(food: string){
        console.log(this.name + '吃' + food);
    }
}

var c = new Cat('小花');
c.eat('老鼠');

介面擴充套件:介面可以繼承介面

interface Animal{
    eat(): void;
}

interface Person extends Animal{
    work(): void;
}

class Web implements Person{
    public name: string;
    constructor(name: string) {
        this.name = name;
    }
    eat() {
        console.log(this.name + '喜歡吃饅頭')
    }
    work() {
        console.log(this.name + '寫程式碼');
    }
}

var w = new Web('小李');
w.eat();
interface Animal{
    eat(): void;
}

interface Person extends Animal{
    work(): void;
}

class Programmer{
    public name: string;
    constructor(name: string) {
        this.name = name;
    }
    coding(code: string) {
        console.log(this.name + code)
    }
}

class Web extends Programmer implements Person{    
    constructor(name: string) {
       super(name)
    }
    eat() {
        console.log(this.name + '喜歡吃饅頭')
    }
    work() {
        console.log(this.name + '寫程式碼');
    }
}

var w = new Web('小李');
w.eat();
w.coding('寫ts程式碼');

五、typeScript中的泛型

  • 泛型的定義
  • 泛型函數
  • 泛型類
  • 泛型介面

泛型:軟體工程中,我們不僅要建立一致的定義良好的API,同時也要考慮可重用性。 元件不僅能夠支援當前的數據型別,同時也能支援未來的數據型別,這在建立大型系統時爲你提供了十分靈活的功能。

在像C#和Java這樣的語言中,可以使用泛型來建立可重用的元件,一個元件可以支援多種型別的數據。 這樣使用者就可以以自己的數據型別來使用元件。

通俗理解:泛型就是解決 類 介面 方法的複用性、以及對不特定數據型別的支援(型別校驗)

只能返回string型別的數據

function getData(value: string): string {
    return value;
}

同時返回 string型別 和number型別 (程式碼冗餘)

function getData1(value: string): string {
    return value;
}

function getData2(value: number): number {
    return value;
}

同時返回 string型別 和number型別 any可以解決這個問題

function getData(value: any): any {
    return '哈哈哈';
}

getData(123);
getData('str');

any放棄了型別檢查,傳入什麼 返回什麼。比如:傳入number 型別必須返回number型別 傳入 string型別必須返回string型別

傳入的參數型別和返回的參數型別可以不一致

function getData(value: any): any {
    return '哈哈哈';
}

泛型:可以支援不特定的數據型別 要求:傳入的參數和返回的參數一直
T表示泛型,具體什麼型別是呼叫這個方法的時候決定的

function getData<T>(value: T): T {
    return value;
}
getData<number>(123);
getData<string>('1214231');
getData<number>('2112');       /*錯誤的寫法*/  
function getData<T>(value:T):any{
	return '2145214214';
}
getData<number>(123);  //參數必須是number
getData<string>('這是一個泛型');

泛型類:比如有個最小堆演算法,需要同時支援返回數位和字串 a - z兩種型別。 通過類的泛型來實現

class MinClass{
     public list:number[] = [];
     add(num: number) {
         this.list.push(num)
     }
     min(): number {
         var minNum = this.list[0];
         for(var i = 0;i< this.list.length; i++){
             if(minNum > this.list[i]){
                 minNum = this.list[i];
             }
         }
         return minNum;
     }
 }

var m = new MinClass();
m.add(3);
m.add(22);
m.add(23);
m.add(6);
m.add(7);
m.min());

類的泛型

class MinClas<T>{
    public list: T[] = [];
    add(value: T): void {
        this.list.push(value);
    }
    min(): T {        
        var minNum = this.list[0];
        for(var i = 0;i < this.list.length; i++){
            if(minNum > this.list[i]){
                minNum = this.list[i];
            }
        }
        return minNum;
    }
}

var m1 = new MinClas<number>();   /* 範例化類 並且制定了類的T代表的型別是number */
m1.add(11);
m1.add(3);
m1.add(2);
m1.min();
var m2=new MinClas<string>();   /* 範例化類 並且制定了類的T代表的型別是string */
m2.add('c');
m2.add('a');
m2.add('v');
m2.min()

函數型別介面

interface ConfigFn{
    (value1: string, value2: string): string;
}
var setData: ConfigFn = function(value1: string, value2: string): string {
    return value1 + value2;
}
setData('name', '張三');

泛型介面

interface ConfigFn{
    <T>(value:T):T;
}
var getData:ConfigFn=function<T>(value:T):T{
    return value;
}
getData<string>('張三');
getData<string>(1243);  //錯誤

泛型介面

interface ConfigFn<T>{
    (value: T): T;
}
function getData<T>(value: T): T {
    return value;
}
var myGetData: ConfigFn<string> = getData;     
myGetData('20');  /*正確*/
myGetData(20)  //錯誤

泛類:泛型可以幫助我們避免重複的程式碼以及對不特定數據型別的支援(型別校驗),下面 下麪我們看看把類當做參數的泛型類

1、定義個類
2、把類作爲參數來約束數據傳入的型別

// 比如有個最小堆演算法 普通型別
class MinClass{
    public list: number[] = [];
    add(num: number) {
        this.list.push(num)
    }
    min(): number {
        var minNum = this.list[0];
        for(var i = 0;i< this.list.length; i++){
            if(minNum > this.list[i]){
                minNum = this.list[i];
            }
        }
        return minNum;
    }
}
var m1 = new MinClass();   
m1.add(11);
m1.add(3);
m1.add(2);
m1.min()

類的泛型

class MinClas<T>{
    public list: T[] = [];
    add(value: T): void {
        this.list.push(value);
    }
    min(): T {        
        var minNum = this.list[0];
        for(var i = 0;i< this.list.length; i++){
            if(minNum > this.list[i]){
                minNum = this.list[i];
            }
        }
        return minNum;
    }
}

var m1 = new MinClas<number>();   //範例化類 並且指定了類的T代表的型別是number
m1.add(11);
m1.add(3);
m1.add(2);
m1.min()

var m2=new MinClas<string>();  //範例化類 並指定定了類的T代表的型別是string
m2.add('c');
m2.add('a');
m2.add('v');
m2.min()

定義一個User的類這個類的作用就是對映數據庫欄位
然後定義一個 MysqlDb的類這個類用於操作數據庫
然後把User類作爲參數傳入到MysqlDb中

var user=new User({
    username:'張三',
    password:'123456'
})

var Db=new MysqlDb();
Db.add(user);

把類作爲參數來約束數據傳入的型別

class User{
    username:string | undefined;
    pasword:string | undefined;
}

class MysqlDb{
    add(user:User):boolean{
        console.log(user);
        return true;
    }
}
var u = new User();
u.username = '張三';
u.pasword = '123456';
var Db = new MysqlDb();
Db.add(u);

class ArticleCate{
    title: string | undefined;
    desc: string | undefined;
    status: number | undefined
}

class MysqlDb{
    add(info: ArticleCate): boolean{
        console.log(info);
        console.log(info.title);
        return true;
    }

}
var a = new ArticleCate();
a.title = "國內";
a.desc = "國內新聞";
a.status = 1;

var Db = new MysqlDb();
Db.add(a);
//定義操作數據庫的泛型類
class MysqlDb<T>{
    add(info: T): boolean {
        console.log(info);       
        return true;
    }
    updated(info: T, id: number): boolean {
        console.log(info);  
        console.log(id); 
        return true;
    }
}


// 想給User表增加數據
// 1、定義一個User類 和數據庫進行對映
class User{
    username: string | undefined;
    pasword: string | undefined;
}
var u = new User();
u.username = '張三';
u.pasword = '123456';
var Db = new MysqlDb<User>();
Db.add(u);
// 2、相關ArticleCate增加數據  定義一個ArticleCate類 和數據庫進行對映
class ArticleCate{
    title: string | undefined;
    desc: string | undefined;
    status: number | undefined;
    constructor(params: {
        title: string | undefined,
        desc: string | undefined,
        status?: number | undefined
    }){
        this.title = params.title;
        this.desc = params.desc;
        this.status = params.status;
    }
}
// 增加操作
var a = new ArticleCate({
    title: '分類',
    desc: '1111',
    status: 1
});

// 類當做參數的泛型類
var Db = new MysqlDb<ArticleCate>();
Db.add(a);
//修改數據
var a = new ArticleCate({
    title: '分類111',
    desc: '2222'      
});
a.status = 0;
var Db = new MysqlDb<ArticleCate>();
Db.updated(a, 12);

案例展示 :

功能:定義一個操作數據庫的庫 支援 Mysql Mssql MongoDb
要求1:Mysql MsSql MongoDb功能一樣 都有 add update delete get方法
注意:約束統一的規範、以及程式碼重用
解決方案:需要約束規範所以要定義介面 ,需要程式碼重用所以用到泛型
1、介面:在物件導向的程式設計中,介面是一種規範的定義,它定義了行爲和動作的規範
2、泛型 通俗理解:泛型就是解決 類 介面 方法的複用性、

interface DBI<T>{
    add(info:T): boolean;
    update(info: T, id: number): boolean;
    delete(id: number): boolean;
    get(id: number): any[];
}

定義一個操作mysql數據庫的類

注意:要實現泛型介面 這個類也應該是一個泛型類

class MysqlDb<T> implements DBI<T>{
    constructor(){
        console.log('數據庫建立連線');
    }
    add(info: T): boolean {
        console.log(info);
        return true;
    }    
    update(info: T, id: number): boolean {
        throw new Error("Method not implemented.");
    }
    delete(id: number): boolean {
        throw new Error("Method not implemented.");
    }
    get(id: number): any[] {
        var list = [
            {
                title:'xxxx',
                desc:'xxxxxxxxxx'
            },
            {
                title:'xxxx',
                desc:'xxxxxxxxxx'
            }
        ]
        return list;
    }
}

定義一個操作mssql數據庫的類

class MsSqlDb<T> implements DBI<T>{
    constructor(){
        console.log('數據庫建立連線');
    }
    add(info: T): boolean {
        console.log(info);
        return true;
    }    
    update(info: T, id: number): boolean {
        throw new Error("Method not implemented.");
    }
    delete(id: number): boolean {
        throw new Error("Method not implemented.");
    }
    get(id: number): any[] {
        var list=[
            {
                title:'xxxx',
                desc:'xxxxxxxxxx'
            },
            {
                title:'xxxx',
                desc:'xxxxxxxxxx'
            }
        ]
        return list;
    }
}

操作使用者表 定義一個User類和數據表做對映

class User{
    username: string | undefined;
    password: string | undefined;
}
var u = new User();
u.username = '張三111';
u.password = '123456';
var oMysql = new MysqlDb<User>(); //類作爲參數來約束數據傳入的型別 
oMysql.add(u);

class User{
    username: string | undefined;
    password: string | undefined;
}

var u = new User();
u.username = '張三2222';
u.password = '123456';

var oMssql = new MsSqlDb<User>();
oMssql.add(u);

//獲取User表 ID=4的數據
var data = oMssql.get(4);
console.log(data);

六、名稱空間

在程式碼量較大的情況下,爲了避免各種變數命名相沖突,可將相似功能的函數、類、介面等放置到名稱空間內

同Java的包、.Net的名稱空間一樣,TypeScript的名稱空間可以將程式碼包裹起來,只對外暴露需要在外部存取的物件。名稱空間內的物件通過export關鍵字對外暴露。

名稱空間和模組的區別:

名稱空間:內部模組,主要用於組織程式碼,避免命名衝突。
模 塊:ts的外部模組的簡稱,側重程式碼的複用,一個模組裡可能會有多個名稱空間。

// modules/animal.ts
export namespace A{
    interface Animal {
        name: string;
        eat(): void;
    }
    export class Dog implements Animal {
        name: string;
        constructor(theName: string) {
            this.name = theName;
        }

        eat() {
            console.log(`${this.name} 在吃狗糧。`);
        }
    }

    export class Cat implements Animal {
        name: string;
        constructor(theName: string) {
            this.name = theName;
        }

        eat() {
            console.log(`${this.name} 吃貓糧。`);
        }
    }   
}

export namespace B{
    interface Animal {
        name: string;
        eat(): void;
    }
    export class Dog implements Animal {
        name: string;
        constructor(theName: string) {
            this.name = theName;
        }

        eat() {
            console.log(`${this.name} 在吃狗糧。`);
        }
    }

    export class Cat implements Animal {
        name: string;
        constructor(theName: string) {
            this.name = theName;
        }

        eat() {
            console.log(`${this.name} 在吃貓糧。`);
        }
    }   
}

名稱空間和模組的區別:

名稱空間:內部模組,主要用於組織程式碼,避免命名衝突。
模 塊:ts的外部模組的簡稱,側重程式碼的複用,一個模組裡可能會有多個名稱空間。

import { A, B } from './modules/animal';
var d = new A.Dog('小黑');
d.eat();

var dog = new B.Dog('小花');
dog.eat();

七、裝飾器

裝飾器:裝飾器是一種特殊型別的宣告,它能夠被附加到類宣告,方法,屬性或參數上,可以修改類的行爲。

通俗的講裝飾器就是一個方法,可以注入到類、方法、屬性參數上來擴充套件類、屬性、方法、參數的功能。
常見的裝飾器有:類裝飾器、屬性裝飾器、方法裝飾器、參數裝飾器
裝飾器的寫法:普通裝飾器(無法傳參) 、 裝飾器工廠(可傳參)
裝飾器是過去幾年中js最大的成就之一,已是Es7的標準特性之一

1、 類裝飾器:普通裝飾器(無法傳參)

類裝飾器:類裝飾器在類宣告之前被宣告(緊靠着類宣告)。 類裝飾器應用於類建構函式,可以用來監視,修改或替換類定義。 傳入一個參數

function logClass(params: any) {
    console.log(params);
    // params 就是當前類
    params.prototype.apiUrl = '動態擴充套件的屬性';
    params.prototype.run = function() {
        console.log('我是一個run方法');
    }

}

@logClass
class HttpClient{
    constructor(){
    }
    getData(){

    }
}
var http:any=new HttpClient();
console.log(http.apiUrl);
http.run();
2、 類裝飾器:裝飾器工廠(可傳參)
function logClass(params: string) {
    return function(target: any) {
        console.log(target);
        console.log(params);
        target.prototype.apiUrl = params;
    }
}

@logClass('http://www.itying.com/api')
class HttpClient{
    constructor() {
    }

    getData(){
    }
}

var http:any=new HttpClient();
console.log(http.apiUrl);
3、 類裝飾器

下面 下麪是一個過載建構函式的例子。
類裝飾器表達式會在執行時當作函數被呼叫,類別建構函式作爲其唯一的參數。
如果類裝飾器返回一個值,它會使用提供的建構函式來替換類的宣告。

function logClass(target:any){
    console.log(target);
    return class extends target{
        apiUrl:any='我是修改後的數據';
        getData(){
            this.apiUrl=this.apiUrl+'----';
            console.log(this.apiUrl);
        }
    }
}


@logClass
class HttpClient{
    public apiUrl:string | undefined;
    constructor(){
        this.apiUrl='我是建構函式裏面的apiUrl';
    }
    getData(){
        console.log(this.apiUrl);
    }
}

var http=new HttpClient();
http.getData();
4、屬性裝飾器

屬性裝飾器表達式會在執行時當作函數被呼叫,傳入下列2個參數:
1、對於靜態成員來說是類別建構函式,對於範例成員是類的原型物件。
2、成員的名字。

//類裝飾器
function logClass(params:string){
     return function(target:any){
         // console.log(target);
         // console.log(params);       
         
     }
 }

//屬性裝飾器
function logProperty(params:any){
    return function(target:any,attr:any){
        console.log(target);
        console.log(attr);
        target[attr]=params;
    }
}
@logClass('xxxx')
class HttpClient{
    @logProperty('http://itying.com')
    public url:any |undefined;
    constructor(){
    }
    getData(){
        console.log(this.url);
    }
}
var http=new HttpClient();
http.getData();
5、方法裝飾器

它會被應用到方法的 屬性描述符上,可以用來監視,修改或者替換方法定義。
方法裝飾會在執行時傳入下列3個參數:
1、對於靜態成員來說是類別建構函式,對於範例成員是類的原型物件。
2、成員的名字。
3、成員的屬性描述符。

// 方法裝飾器一
function get(params: any){
    return function(target: any, methodName: any,desc: any){
        console.log(target);
        console.log(methodName);
        console.log(desc);
        target.apiUrl = 'xxxx';
        target.run = function(){
            console.log('run');
        }
    }
}

class HttpClient{  
    public url: any |undefined;
    constructor() {
    }
    @get('http://www.itying,com')
    getData() {
        console.log(this.url);
    }
}

var http:any = new HttpClient();
console.log(http.apiUrl);
http.run();
// 方法裝飾器二
function get(params: any){
    return function(target: any, methodName: any,desc: any){
        console.log(target);
        console.log(methodName);
        console.log(desc.value);       
        //修改裝飾器的方法  把裝飾器方法裏面傳入的所有參數改爲string型別
        //1、儲存當前的方法
        var oMethod = desc.value;
        desc.value = function(...args: any[]){                
            args = args.map((value) => {
                return String(value);
            })
            oMethod.apply(this, args);
        }
    }
}

class HttpClient{  
    public url: any | undefined;
    constructor() {
    }
    @get('http://www.itying,com')
    getData(...args: any[]) {
        console.log(args);
        console.log('我是getData裏面的方法');
    }
}

var http = new HttpClient();
http.getData(123, 'xxx');
6、方法參數裝飾器

參數裝飾器表達式會在執行時當作函數被呼叫,傳入下列3個參數:
1、對於靜態成員來說是類別建構函式,對於範例成員是類的原型物件。
2、參數的名字。
3、參數在函數參數列表中的索引。

function logParams(params:any){
    return function(target: any, methodName: any, paramsIndex: any) {
        console.log(params);
        console.log(target);
        console.log(methodName);
        console.log(paramsIndex);
        target.apiUrl = params;
    }   
}

class HttpClient{  
      public url: any |undefined;
       constructor() {
      }           
      getData(@logParams('xxxxx') uuid: any){               
          console.log(uuid);
      }
}
var http:any = new HttpClient();
http.getData(123456);
console.log( http.apiUrl);

裝飾器執行順序
屬性》方法》方法參數》類
如果有多個同樣的裝飾器,它會先執行後面的

function logClass1(params: string) {
    return function(target: any) {
      console.log('類裝飾器1')
    }
}

function logClass2(params: string) {
    return function(target: any) {
      console.log('類裝飾器2')
    }
}

function logAttribute1(params?: string) {
    return function(target: any, attrName: any) {
      console.log('屬性裝飾器1')
    }
}

function logAttribute2(params?: string) {
    return function(target: any, attrName: any) {
      console.log('屬性裝飾器2')
    }
}

function logMethod1(params?: string) {
    return function(target: any, attrName: any, desc: any) {
      console.log('方法裝飾器1')
    }
}

function logMethod2(params?: string) {
    return function(target: any, attrName: any, desc: any) {
      console.log('方法裝飾器2')
    }
}

function logParams1(params?: string) {
    return function(target: any, attrName: any, desc: any) {
      console.log('方法參數裝飾器1')
    }
}

function logParams2(params?: string) {
    return function(target: any, attrName: any, desc: any) {
      console.log('方法參數裝飾器2')
    }
}

@logClass1('http://www.itying.com/api')
@logClass2('xxxx')
class HttpClient{
    @logAttribute1()
    @logAttribute2()
    public apiUrl: string | undefined;
    constructor() {
    }

    @logMethod1()
    @logMethod2()
    getData() {
        return true;
    }

    setData(@logParams1() attr1: any, @logParams2() attr2: any,) {
    }
}

var http:any=new HttpClient();