TypeScript系列文件

Staticy發表於2019-02-20

2019年,typescript註定成為一個很熱門的前端技術,因為vue3.0用typescript來寫整個框架,很多框架和庫都用了typescript,deno是不是說你?還有react很火的ui框架antd-design也用typescript,趕緊抓緊typescript吧!

這可能是目前為止最全最好學習的typescirpt教程,持續更新中,程式碼和文件都在github中,歡迎github star

為什麼要學習和使用TypeScript?

TypeScript是一種純物件導向的語言。相比JavaScript,它更能提高我們的開發效率。而且,由於TypeScript是JavaScript的超集,TypeScript能夠和JavaScript混合使用。因為TypeScript的這個強大特性,越來越多的優秀框架開始嘗試使用TypeScript。

在編寫程式碼的階段,TypeScript就能夠找到大部分的錯誤,而JavaScript在這方面就沒那麼友好了。要知道,執行時錯誤越少,你的程式的bug就越少。除此之外,相比JavaScript,TypeScript的重構也更容易。

供參考文件:

TypeScript預覽

一、TypeScript介紹、安裝和編譯

TypeScript 介紹

1.TypeScript 是由微軟開發的一款開源的程式語言,像後端 java、C#這樣的面嚮物件語言可以讓 js 開發大型企業專案。

2.TypeScript 是 Javascript的超級,遵循最新的 ES6、Es5 規範(相當於包含了es6、es5的語法)。TypeScript擴充套件了JavaScript的語法。

3.最新的 Vue 、React 也可以整合 TypeScript。

供參考文件:

TypeScript安裝編譯

  • 安裝nodejs環境,用npm全域性安裝typescript編譯器
npm install -g typescript
複製程式碼
  • typescript手動編譯

typescript檔案字尾名為.ts,最後將編譯成js檔案。ts是js的擴充套件,想要ts程式碼在瀏覽器/Node環境下執行,需要把ts程式碼編譯成js程式碼。

npm安裝typescript後,命令列輸入tsc + 檔名就可以把ts編譯成js。

在index.ts中:

console.log('hello typescript')
複製程式碼

在命令列上,執行typescript編譯器:

tsc index.ts
複製程式碼

輸出結果為一個index.js檔案,它包含了和輸入檔案中相同的javascript程式碼。因為typescript是javascript的超集,完全相容javascript。

TypeScript自動編譯

上面的手動編譯ts很麻煩,配置開發編輯工具Visual Studio Code(Vscode),就可以自動編譯ts。

  1. 命令列輸入tsc --init,會生成配置檔案tsconfig.json,使用預設配置即可。
  • tsconfig.json詳細配置在官網
  1. 在Vscode編輯器中點選選單欄-任務-執行任務(遇到錯誤使用快捷鍵ctrl + shift + b),點選 tsc:監視-tsconfig.json,在index.ts裡面寫入js程式碼並儲存,會自動編譯成index.js。

二、TypeScript資料型別

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

基礎型別

在typescript中主要給我們提供了以下資料型別:

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

相比於js的資料型別,typescript中多了元組型別、列舉型別、任意型別、void型別和never型別。當然這些只是基礎型別,還有更多其他型別,後面的型別推論高階型別可以進一步瞭解。

變數定義

寫ts程式碼變數可以指定其型別,指定型別後賦值必須為指定的型別,否則報錯。

  • 如果沒有指定型別,ts型別推論會幫助提供型別,請看ts型別推論
var flag:boolean = true
flag = 123 // 錯誤,型別不一致
複製程式碼

資料型別

字串型別(string)

var str:string = 'this is ts';

str='haha';  //正確

// str=true;  //錯誤
複製程式碼

數字型別(number)

var num:number = 123;

num = 456; // 正確 

// num='str';    //錯誤
複製程式碼

布林型別(boolean)

var flag:boolean = true

flag = false // 正確

// flag=123;  // 錯誤
複製程式碼

null 和 undefined

undefined:

{
    // 在js中,變數已宣告但未初始化為undefined
    var undefinedTest:number
    // console.log(undefinedTest) // 錯誤寫法,typescript報錯,賦值了才正確

    // 在typescript中,已宣告未初始化的值要直接訪問的話型別需要定義為undefined
    var undefinedTest2:undefined
    console.log(undefinedTest2) // 正確寫法,輸出undefined 
}
{
    // 可能是number型別 可能是undefined
    var undefinedTest3:number | undefined;
    console.log(num);
}
複製程式碼

null:

// null是一個空指標物件,undefined是未初始化的變數。因此,可以把undefined看作是空的變數,而null看作是空的物件
var nullTest:null
nullTest = null
// nullTest = {} // 錯誤,定義了型別是null,值必須為null
複製程式碼

陣列型別(array)

ts有兩種方式可以定義陣列。 第一種,可以在元素型別後面接上 [],表示由此型別元素組成的一個陣列:

// 第一種
var arr:number[] = [1, 2, 3]
複製程式碼

第二種方式是使用陣列泛型,Array<元素型別>:

// 第二種
var arr2:Array<number> = [1, 2, 3]
複製程式碼

元組型別(tuple)

和陣列類似,元素的型別不一樣:

let arr:[number,string] = [123,'this is ts']
複製程式碼

列舉型別(enum)

用法:

enum 列舉名{ 
    識別符號[=整型常數], 
    識別符號[=整型常數], 
    ... 
    識別符號[=整型常數], 
}
複製程式碼
enum Flag {success = 1,error = 2};

let s:Flag = Flag.success // 使用列舉型別中的值
console.log('正確狀態',s)
let f:Flag = Flag.error
console.log('錯誤狀態',f)
複製程式碼

任意型別(any)

為那些在程式設計階段還不清楚型別的變數指定一個型別

var number:any = 123
number = 'str'
number = true
複製程式碼

void型別

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

// 表示方法沒有返回任何型別
function run(): void {
    console.log('run')
}

run()
複製程式碼

never型別

表示的是那些永不存在的值的型別,例如異常

var a:never

// a = 123 //錯誤寫法
a = (() => {
    throw new Error('錯誤');
})()
複製程式碼

三、TypeScript函式

內容概述: 函式的定義、可選引數、預設引數、剩餘引數、函式過載、箭頭函式。

函式的定義

語法:

// 函式宣告
function fn(x: Type, y: Type): Type {}

// 函式表示式
var fn = (x: Type, y: Type): Type => {}

// 函式表示式:指定變數fn的型別
var fn: (x: Type, y: Type) => Type = (x, y) => {}
複製程式碼
  • 定義函式有函式宣告和函式表示式兩種形式。定義函式的引數和返回值可以指定其型別;當呼叫函式時,傳入引數型別必須與定義函式引數型別保持一致。
// 函式宣告法
function run(x: number, y: number): number {
    return x + y;
}

// 函式表示式法
var run2 = (x: number, y: number): string => {
    return 'run2'
}

run(1, 2);
run2(1, 2);
複製程式碼

這段程式碼中,函式run和run2指定了引數型別,呼叫時傳入引數型別必須保持一致。

  • 函式表示式法另外一種寫法
var run3: (x: number, y: number) => string = function(x: number, y: number): string{
    return 'run3';
}
run3(1, 2);
複製程式碼

當給變數run3指定型別的時候,應該是函式的引數和返回值的約束型別。如果用後面學到的ts型別推論,可以簡寫為:

var run4: (x: number, y: number) => string = function(x, y){ // 型別推論可以確定函式的引數和返回值型別,也就可以省略型別指定
    return 'run4';
}
run4(1, 2);
複製程式碼
  • 函式沒有返回值用void型別指定返回值型別
function voidFnc(): void{
    console.log('沒有返回值的方法用void')
}
voidFnc();
複製程式碼

可選引數

  • es5裡面方法的實參和行參可以不一樣,但是ts中必須一樣,如果不一樣就需要在可選引數後加?,這就是可選引數。
function electParam(name:string, age?:number):string {
    // 這裡的age可傳可不傳,age就是可選引數
    if(age){
        return `${name} --- ${age}`
    }else{
        return `${name} --- 年齡保密`
    }
}
console.log('可選引數', electParam('dz'))

// 注意: 可選引數必須配置到引數的最後面

// 錯誤寫法:可選引數不在最後面
// function electParam2(name?: string, age: number): string {
//     ...
// }
複製程式碼

預設引數

  • es5裡面沒法設定預設引數,es6和ts中都可以設定預設引數
// age為預設引數
function defaultParam(name:string, age:number = 20):String {
    return `${name} --- ${age}`
}

console.log('預設引數', defaultParam('dz'))
複製程式碼

剩餘引數

  • 當有很多引數時候或引數個數不確定,可以用三點運算子
// sum引數傳過來的是一個陣列
function sum(...result: number[]): number {
    var sum = 0;

    for (var i = 0; i < result.length; i++) {

        sum += result[i];
    }

    return sum;
}
console.log('剩餘引數', sum(1, 2, 3, 4, 5, 6));

// a=1 b=2 其他引數為剩餘引數
function sum2(a: number, b: number, ...result: number[]): number {
    var sum = a * b;

    for (var i = 0; i < result.length; i++) {

        sum += result[i];
    }

    return sum;
}
console.log('剩餘引數2', sum2(1, 2, 3, 4, 5, 6));
複製程式碼

函式過載

同名函式,傳入不同的引數,實現不同的功能,這就叫作函式過載。

  • java中方法的過載:過載指的是兩個或者兩個以上同名函式,但它們的引數不一樣,這時會出現函式過載的情況。
  • typescript中的過載:通過為同一個函式提供多個函式型別定義來實現多種功能的目的。
  • ts為了相容es5以及es6,過載的寫法和java中有區別。

es5中同名函式,後面會覆蓋前面的函式,ts中則不會:

function overloadingFn(x: number, y: number): number;
function overloadingFn(x: string, y: string): string;

// 上面定義函式的格式,下面定義函式的具體實現
function overloadingFn(x: any, y: any): any {
    return x + y;
}

overloadingFn(1, 2);
overloadingFn('a', 'b');
複製程式碼

這段程式碼中,同名函式overloadingFn首先定義兩個函式的格式,然後再去實現功能,原來要傳入不同型別引數要用多個函式實現,現在可以用同名函式來實現,這就是函式過載。

箭頭函式

箭頭函式和es6中一樣

setTimeout(() => {
    console.log('箭頭函式')
}, 1000);
複製程式碼

四、TypeScript類

es5中的類

內容概述:類的建立、靜態方法、繼承(物件冒充繼承,原型鏈繼承,物件冒充 + 原型鏈組合繼承)

es5中的物件導向、建構函式、原型與原型鏈本質可以看這個文件caibaojian.com/javascript-… , 個人覺得寫得很清晰。

1.1 類的建立

es5類在建構函式和原型鏈裡都可以新增屬性和方法,原型鏈上的屬性會被多個例項所共享,而建構函式則不會。


function Person() {
    this.name = 'Ming'
    this.run = function() {
        console.log(this.name + '在運動')
    }
}

Person.prototype.sex = '男' // 原型鏈上的屬性會被多個例項所共享
Person.prototype.work = function() {
    console.log(this.name + '在工作')
}


var p = new Person()
p.run()
p.work()
console.log(p.name)

複製程式碼

1.2 靜態方法

呼叫靜態方法不需要例項化


Person.getInfo=function(){
    console.log('我是靜態方法');
}
Person.getInfo();

複製程式碼

1.3 實現繼承

物件冒充(或者叫建構函式繼承)繼承:可以繼承建構函式裡面的屬性和方法,但是沒法繼承原型鏈上面的屬性和方法

原型繼承:可以繼承建構函式裡面的屬性和方法,也可以繼承原型鏈上面的屬性和方法,但是例項化子類的時候沒法給父類傳參

下面是通過物件冒充 + 原型鏈組合繼承,解決了上面兩種繼承方式存在的問題


function Worker(name,age){
    this.name=name;  /*屬性*/
    this.age=age;
    this.run=function(){  /*例項方法*/
        alert(this.name+'在運動');
    }

}      
Worker.prototype.sex="男";
Worker.prototype.work=function(){
    alert(this.name+'在工作');
}
    
function Web(name,age){
    Worker.call(this,name,age);  // 物件冒充繼承,可以繼承建構函式裡面的屬性和方法,例項化子類可以給父類傳參
}
// Web.prototype = new Worker();  // 原型鏈繼承方法一:繼承Worker建構函式和原型上所有的方法和屬性
Web.prototype = Worker.prototype;  //原型鏈繼承方法二:優化了方法一重複繼承建構函式屬性和方法的問題(本質可以看看http://caibaojian.com/javascript-object-5.html)

var w = new Web('趙四',20);   
w.run();
w.work();

複製程式碼

從上面可以看出,物件冒充繼承是在子類Web建構函式裡面通過call方法繼承父類Worker的建構函式的屬性和方法;原型鏈繼承通過子類Web的原型物件等於父類Worker的原型物件來實現繼承;最後這兩種繼承的組合方式實現了完美繼承。

typescript中的類

內容概述: ts中類的定義、繼承、類修飾符、靜態屬性和靜態方法、多型、抽象類和抽象方法

2.1 ts中類的定義

ts中類的定義和es6類的定義一樣


class PersonDefine {
    name: string // 屬性,前面省略了public關鍵詞
    constructor(name:string) { //建構函式
        this.name = name
    }
    run():string { // 原型
        return `${this.name}在運動`
    }
}
var define = new PersonDefine('類的定義')
alert(define.run())

複製程式碼

2.2 繼承

ts中繼承比es5簡單很多,用extends super實現繼承


class WebExtend extends PersonDefine {
    constructor(name:string) {
        super(name) // super繼承父類的建構函式,並向父類建構函式傳參
    }
    work():string {
        return `${this.name}在工作`
    }
}

var extend = new WebExtend('繼承')
alert(extend.run())
alert(extend.work())

複製程式碼

2.3 ts類裡面的修飾符

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

  • public: 公有修飾符,在當前類裡面、子類、類外面都可以訪問
  • protected:保護型別,在當前類裡面、子類裡面可以訪問,在類外部沒法訪問
  • private :私有修飾符,在當前類裡面可以訪問,子類、類外部都沒法訪問

注意:屬性如果不加修飾符,預設就是公有修飾符


// 以private為例
class PersonPrivate{
    private name:string;  /*被private修飾的屬性 => 私有屬性*/
    constructor(name:string){
        this.name=name;
    }
    run():string{
        return `${this.name}在運動` // 私有屬性只能在當前類裡面可以訪問
    }
}

class Web extends PersonPrivate{
    constructor(name:string){
        super(name)
    }
    work(){
        // return `${this.name}在工作` // 報錯,子類不能訪問父類的私有屬性
    }
}
 
var privateName = new PersonPrivate('private')
alert(privateName.run())
// console.log(privateName.name) // 報錯,外部不能訪問類的私有屬性

複製程式碼

2.4靜態屬性和靜態方法

為什麼要用靜態屬性和靜態方法?jq裡面的$.ajax就是用的靜態方法


function $(element) {
    return new Base(element)
}

function Base(element) {
    this.element = document.getElementById(element)
    this.css = function(arr, value) {
        this.element.style[arr] = value
    }
}
$('box').css('color','red')
$.ajax = function() {}  // 想要在$上使用方法怎麼辦,用靜態方法

複製程式碼

ts中實現靜態屬性和靜態方法用static


class PersonStatic{
    /*公有屬性*/
    public name:string;
    constructor(name:string) {
        this.name=name;
    }
    /*例項方法(需要被例項化,所以為例項方法)*/
    run(){  
        return `${this.name}在運動`
    }
    /*靜態屬性*/
    static sex = '男'
    /*靜態方法,裡面沒法直接呼叫類裡面的屬性*/
    static info(){  
        // return 'info方法' + this.name  // 靜態方法不能呼叫本類的方法和屬性,可以呼叫靜態屬性
        return 'info方法' + PersonStatic.sex
    }
}

console.log('靜態方法' + PersonStatic.info())
console.log('靜態屬性' + PersonStatic.sex)

複製程式碼

2.5多型

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

  • 多型屬於繼承

比如定義一個父類Animal,裡面的eat方法不去實現,讓子類Dog和Cat分別實現自己的eat方法


class Animal {
    name:string;
    constructor(name:string) {
        this.name=name;
    }
    eat(){   // eat方法繼承它的子類去實現
    }
}
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+'吃老鼠'
    }
}

複製程式碼

2.6抽象類和抽象方法

定義:用abstract關鍵字定義抽象類和抽象方法,抽象類中的抽象方法不包含具體實現並且必須在派生類(抽象類的子類)中實現

  • 抽象類:它是提供其他類繼承的基類,不能直接被例項化,子類繼承可以被例項化
  • abstract修飾的方法(抽象方法)只能放在抽象類裡面
  • 抽象類和抽象方法用來定義標準(比如定義標準為:抽象類Animal有抽象方法eat,要求它的子類必須包含eat方法)

abstract class AnimalAbst{
    public name:string;
    constructor(name:string){
        this.name=name;
    }
    abstract eat():any;  //抽象方法不包含具體實現並且必須在派生類中實現
    run(){
        console.log('其他方法可以不實現')
    }
}
// var a = new Animal() /*錯誤的寫法,抽象類不能被例項化*/

class DogAbst extends Animal{
    //抽象類的子類必須實現抽象類裡面的抽象方法
    constructor(name:any){
        super(name)
    }
    eat(){
        return this.name + '吃糧食'
    }
}

var d = new DogAbst('小花花');
console.log('抽象類和抽象方法',d.eat());

複製程式碼

五、TypesSript介面

介面定義:介面是對傳入引數進行約束;或者對類裡面的屬性和方法進行宣告和約束,實現這個介面的類必須實現該介面裡面屬性和方法;typescript中的介面用interface關鍵字定義。

介面作用:介面定義了某一批類所需要遵守的規範,介面不關心這些類的內部狀態資料,也不關心這些類裡方法的實現細節,它只規定這批類裡必須提供某些方法,提供這些方法的類就可以滿足實際需要。typescrip中的介面類似於java,同時還增加了更靈活的介面型別,包括屬性、函式、可索引和類等。

內容概述:介面分類:(屬性介面、函式型別介面、可索引介面、類型別介面),介面的繼承

1. 介面分類

1.1 屬性介面

對傳入物件的約束(也就是對json的約束)

在瞭解介面之前,我們來看看函式傳入obj引數


function printLabel(labelInfo: {label:string}){
    return labelInfo
}
// printLabel({name:'obj'});  //錯誤的寫法
console.log(printLabel({label: 'obj'}))

複製程式碼

和上面類似,由此引入屬性介面 => 對方法傳入引數進行約束

下面為屬性介面的例子,方法printFullName對傳入引數FullName(為物件)進行約束


interface FullName{
    firstName: string; // 注意;結束
    secondName: string;
    age?: number // 介面的可選屬性用?
}

function printFullName(name:FullName) {
    // 傳入物件必須包含firstName和secondName,可傳可不傳age
    return name
}
var obj = {
    firstName:'小',
    secondName:'明',
    age: 20
}
console.log(printFullName(obj))

複製程式碼

屬性介面應用:原生js封裝ajax


interface Config{
    type: string;
    url: string;
    data?: string;
    dataType: string;
}
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) {
            if(config.dataType == 'json'){
                console.log(JSON.parse(xhr.responseText))
            }else{
                console.log(xhr.responseText)
            }
        }
    }
}

ajax({
    type: 'get',
    data: 'name=xiaoming',
    url: 'http://a.itying.com/api/productlist',
    dataType: 'json'
})

複製程式碼

1.2 函式型別介面

對方法傳入的引數以及返回值進行約束


interface encrypt{
    (key: string, value: string): string; // 傳入的引數和返回值的型別
}

var md5:encrypt = function(key:string, value:string):string{
    // encrypt對加密方法md5進行約束,同時md5方法的引數和返回值型別和encrypt要保持一致
    return key + value
}

console.log(md5('name', '小明'))

複製程式碼

1.3 可索引介面

對索引和傳入引數的約束(一般用於對陣列、物件的約束)

ts中定義陣列:


var arr1:number[] = [1,2]
var arr2:Array<string> = ['1', '2']

複製程式碼

現在用介面來實現:


// 對陣列的的約束
interface UserArr{
    // 索引為number,引數為string
    [index:number]: string
}
var userarr:UserArr = ['a', 'b']
console.log(userarr)

複製程式碼

// 物件的約束
interface UserObj{
    // 索引為string,引數為string
    [index:string]: string
}
var userobj:UserObj = { name: '小明', sex: '男' }
console.log(userobj)

複製程式碼

1.4 類型別介面

對類的約束,和抽象類抽象有點相似


interface Animal{
    // 對類裡面的屬性和方法進行約束
    name:string;
    eat(str:string):void;
}
// 類實現介面要用implements關鍵字,必須實現介面裡面宣告的方法和屬性
class Cat implements Animal{
    name:string;
    constructor(name:string){
        this.name = name
    }
    eat(food:string){
        console.log(this.name + '吃' + food)
    }
}
var cat = new Cat('小花')
cat.eat('老鼠')

複製程式碼

2. 介面的繼承

和類的繼承一樣,用extends實現介面繼承

下面同時實現類的繼承和介面的繼承


interface Animal {
    eat(): void;
}
// 繼承Animal介面,則實現Person介面的類必須也實現Animal介面裡面的方法
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程式碼');

複製程式碼

六、TypesSript泛型

泛型定義:泛型定義:泛型就是解決類、介面、方法的複用性,以及對不特定資料型別的支援(型別校驗)。ts中用T表示泛型。

泛型公式: 表示泛型,呼叫的時候指定T的資料型別

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

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

內容概述:內容概述:函式的泛型、類的泛型、泛型介面

1. 函式的泛型

傳入的引數型別和返回的引數型別可以指定

我們來看看函式用ts資料型別,想要同時返回string型別和number型別


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

複製程式碼

這樣要寫不同的函式,不能按照需求返回不同型別資料,造成程式碼冗餘 => 由此引入泛型

表示泛型,呼叫的時候指定T的資料型別


function dataT<T>(value:T):T{
    // 傳入引數為T 返回值為T
    return value
}
dataT<number>(1) // 呼叫指定泛型為number型別,則傳入引數也必須為number型別
dataT<string>('string')

function dataAny<T>(value:T):any{
    return '傳入引數為T,任意型別返回值';
}
dataAny<number>(123); // 引數必須是number
dataAny<string>('這是一個泛型');

複製程式碼

2. 類的泛型

也是用來實現類的泛型,new的時候指定T的資料型別

有個最小堆演算法,需要同時支援返回數字和字串兩種型別

使用泛型之前:只能在類的類部指定資料型別,實現需求還要寫一套string型別的類


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(1);
m.add(2);
alert(m.min());

複製程式碼

使用泛型之後:只用一套類來實現


class MinClassT<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 MinClassT<number>();   /*例項化類 並且指定了類的T代表的型別是number*/
m.add(1);
m.add(2);
alert(m1.min())

var m2=new MinClassT<string>();   /*例項化類 並且指定了類的T代表的型別是string*/
m2.add('c');
m2.add('a');
alert(m2.min())

複製程式碼

3. 泛型介面

有一個函式型別介面


interface ConfigFn{
    (value:string):string;
}
var setData:ConfigFn = function(value:string):string{
    return value
}
setData('name');
// setData(20); // 錯誤

複製程式碼

setData(20);寫法錯誤,想要傳入number型別的引數又要寫一個函式型別介面 => 用泛型介面

泛型介面有兩種寫法:


// 泛型介面定義方式一
interface ConfigFnOne{
    <T>(value:T):T;
}
var setDataOne:ConfigFnOne = function<T>(value:T):T{
    return value
}
// 既可以傳入string也可以傳入number型別引數
setDataOne<string>('name');
setDataOne<number>(20);

複製程式碼

// 泛型介面定義方式二
interface ConfigFnTwo<T>{
    (value:T):T;
}
function setDataTwo<T>(value:T):T{
    return value
}
var setDataTwoFn:ConfigFnTwo<string> = setDataTwo
setDataTwoFn('name');

複製程式碼

七、TypesSript型別推論

有的時候不一定需要強制使用型別宣告,在有些沒有明確指出型別的地方,ts型別推論會幫助提供型別。

內容概述:變數初始化型別推論、上下文型別推論。

變數初始化

ts會根據變數初始化的時候賦予的值進行型別推斷。

let a = '型別推論';
// a = true; // Type 'true' is not assignable to type 'string'
複製程式碼

上面程式碼中,a初始化沒有指定型別,ts會推論出a的型別為string,當a = true重新賦值的時候型別不匹配會報相應錯誤,vscode編譯器會提示錯誤。

上下文推斷

ts也會根據上下文進行型別的推斷,比如在事件函式中,函式的第一個引數會根據當前繫結的事件型別推斷處理事件物件。

document.onkeydown = function(e) {
    // console.log(e.button);  //<- Error Property 'button' does not exist on type 'KeyboardEvent'
};
複製程式碼

這個例子會得到一個型別錯誤,ts型別檢查器根據當前繫結的事件類onkeydown自動推導e的型別為KeyboardEvent,vscode編譯器裡滑鼠放上去就有e推匯出來的型別(e:KeyboardEvent)

相關文章