日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

TypeScript基本概念

發(fā)布時間:2024/9/18 综合教程 48 生活家
生活随笔 收集整理的這篇文章主要介紹了 TypeScript基本概念 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

TypeScript

TypeScript簡稱TS,TS和JS之間的關(guān)系其實就是Less/Sass和CSS之間的關(guān)系,就像Less/Sass是對CSS進行擴展一樣,TS也是對JS進行擴展,就像Less/Sass最終會轉(zhuǎn)換成CSS一樣,我們編寫好的TS代碼最終也會換成JS。

因為JavaScript是弱類型,很多錯誤只有在運行時才會被發(fā)現(xiàn),而TypeScript是強類型,它提供了一套靜態(tài)檢測機制,可以幫助我們在編譯時就發(fā)現(xiàn)錯誤。

TypeScript特點

支持最新的JavaScript新特特性;支持代碼靜態(tài)檢查;支持諸如C、C++、Java、Go等后端語言中的特性(枚舉、泛型、類型轉(zhuǎn)換、命名空間、聲明文件、類、接口等)。


配置webpack打包ts

1.初始化一個自動打包的webpack項目
2.通過tsc --init初始化TypeScript配置文件 (自動生成tsconfig.json)
3.通過npm install typescript ts-loader安裝對應(yīng)loader
4.修改webpack配置文件
entry: "./src/js/index.ts",
resolve: {
    extensions: [ '.tsx', '.ts', '.js' ] // 支持識別ts文件
},
rules: [
    { // ts編譯配置
        test: /.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/
    }
]

基礎(chǔ)類型

TypeScript支持與JavaScript幾乎相同的數(shù)據(jù)類型,此外還提供了實用的枚舉類型方便我們使用。

數(shù)值類型 number: let val1:number; 定義了一個名稱叫做val1的變量, 這個變量中將來只能存儲數(shù)值類型的數(shù)據(jù)
布爾類型 boolean:let val2:boolean;
字符串類型 string:let val3:string;
數(shù)組類型Array:let arr1:Array<number>; arr1 = [1, 3]; let arr2:string[]; arr2 = ['a', 'b'];
聯(lián)合類型 |:let arr3:(number | string)[]; 表示定義了一個名稱叫做arr3的數(shù)組, 這個數(shù)組中既可以存儲數(shù)值類型也可以存儲字符串類型。arr3 = [1, 'b', 2, 'c'];
任意類型 any:
元祖類型:TS中的元祖類型其實就是數(shù)組類型的擴展,元祖用于保存定長定數(shù)據(jù)類型的數(shù)據(jù)。let arr5:[string, number, boolean]; 表示定義了一個名稱叫做arr5的元祖, 這個元祖中將來可以存儲3個元素, 第一個元素必須是字符串類型, 第二個元素必須是數(shù)字類型, 第三個元素必須是布爾類型
void類型:void與any正好相反, 表示沒有任何類型, 一般用于函數(shù)返回值。 在TS中只有null和undefined可以賦值給void類型

注意點: nullundefined是所有類型的子類型, 所以我們可以將nullundefined賦值給任意類型


擴展類型

枚舉類型

枚舉類型是TS為JS擴展的一種類型,在原生的JS中是沒有枚舉類型的,枚舉用于表示固定的幾個取值。TS中的枚舉底層實現(xiàn)的本質(zhì)其實就是數(shù)值類型。

注意點: TS中的枚舉類型的取值,默認(rèn)是從上至下從0開始遞增的,雖然默認(rèn)是從0開始遞增的,但是我們也可以手動的指定枚舉的取值的值;如果手動指定了前面枚舉值的取值,那么后面枚舉值的取值會根據(jù)前面的值來遞增。

enum Gender{
    Male,
    Femal
}
let val:Gender;
val = Gender.Male;
// val = 'nan'; // 報錯
// val  = false;// 報錯
// 注意點: TS中的枚舉底層實現(xiàn)的本質(zhì)其實就是數(shù)值類型, 所以賦值一個數(shù)值不會報錯
// val = 666; // 不會報錯
console.log(Gender.Male); // 0
console.log(Gender.Femal);// 1
// 我們通過它對應(yīng)的數(shù)據(jù)拿到它的枚舉值
console.log(Gender[0]); // Male

TS中支持兩種枚舉,一種是數(shù)字枚舉,一種是字符串枚舉。

字符串枚舉

和數(shù)字枚舉不一樣,字符串枚舉不能使用常量或者計算結(jié)果給枚舉值賦值, 但是它可以使用內(nèi)部的其它枚舉值來賦值;如果使用字符串給前面的枚舉值賦值了, 那么后面的枚舉值也必須手動賦值。

enum Gender{
    Male = 'www.baidu.com',
    Female = 'www.taobao.com',
    Yao = Female
}
console.log(Gender.Female);
console.log(Gender.Yao);
枚舉反向映射

可以根據(jù)枚舉值獲取到原始值,也可以根據(jù)原始值獲取到枚舉值。

enum Gender{
    Male,
    Female
}
console.log(Gender.Male); // 0
console.log(Gender[0]); // Male
異構(gòu)枚舉

枚舉中既包含數(shù)字又包含字符串,我們就稱之為異構(gòu)枚舉。

enum Gender{
    Male = 6,
    Female = 'nv'
}
console.log(Gender.Male); // 6
console.log(Gender.Female); // nv
console.log(Gender[6]); // Male
// console.log(Gender['nv']); // 如果是字符串枚舉, 那么無法通過原始值獲取到枚舉值
枚舉成員類型

以把枚舉成員當(dāng)做類型來使用。

enum Gender{
    Male,
    Female
}
interface TestInterface {
    age: Gender.Male
}
class Person implements TestInterface{
    age: Gender.Male
    // age: Gender.Female // 由于類型不匹配, 所以會報錯
    // age: 0 // 由于數(shù)字枚舉的本質(zhì)就是數(shù)值, 所以這里寫一個數(shù)值也不會報錯

//////////////////  如果是字符串枚舉  //////////////////
    // age: Gender.Male
    // age: Gender.Female  // 報錯
    // age: 'www.baidu.com' // 如果是字符串枚舉, 那么只能是枚舉成員的值, 不能是其它的值,報錯
    // age: string // 報錯
}
聯(lián)合枚舉類型

聯(lián)合類型就是將多種數(shù)據(jù)類型通過|連接起來,我們可以把枚舉類型當(dāng)做一個聯(lián)合類型來使用。

enum Gender{
    Male ,
    Female
}
interface TestInterface {
     age: Gender // 會轉(zhuǎn)化為聯(lián)合枚舉類型:age: (Gender.Male | Gender.Female)
}
class Person implements TestInterface{
    // age: Gender.Male
    age: Gender.Female
}
運行時枚舉

枚舉在編譯之后是一個真實存儲的對象,所以可以在運行時使用。 而像接口這種只是用來做約束做靜態(tài)檢查的代碼,編譯之后是不存在的。

常量枚舉

普通枚舉和常量枚舉的區(qū)別:普通枚舉會生成真實存在的對象;常量枚舉不會生成真實存在的對象, 而是利用枚舉成員的值直接替換使用到的地方。

const enum Gender2{ // 不加const就是普通枚舉
    Male,
    Female
}
console.log(Gender2.Male === 0); // 編譯后變成 console.log(0 /* Male */ === 0);
枚舉底層實現(xiàn)原理
var Gender;
(function (Gender) {
 // Gender[key] = value;
    Gender[Gender["Male"] = 0] = "Male";
    Gender[Gender["Femal"] = 1] = "Femal";
})(Gender || (Gender = {}));
// 原理所表示的相當(dāng)于:
let Gender = {};
Gender["Male"] = 0;
Gender[0] = "Male";
Gender["Femal"] = 1;
Gender[1] = "Femal";

Never類型

Never類型 表示的是那些永不存在的值的類型,一般用于拋出異常或根本不可能有返回值的函數(shù)。

function demo():never {
    throw new Error('報錯了');
}
function demo2():never {
    while (true){}
}

Object類型 表示一個對象,let obj:object;


類型斷言

TS中的類型斷言和其它編程語言的類型轉(zhuǎn)換很像,可以將一種類型強制轉(zhuǎn)換成另外一種類型;類型斷言就是告訴編譯器不需要檢查我們指定的語法

例如: 我們拿到了一個any類型的變量,但是我們明確的知道這個變量中保存的是字符串類型,此時我們就可以通過類型斷言告訴編譯器,這個變量是一個字符串類型,此時我們就可以通過類型斷言將any類型轉(zhuǎn)換成string類型,使用字符串類型中相關(guān)的方法了。

// 使用方式1
let str:any = 'test';
let len = (<string>str).length;
// 使用方式2
let len = (str as string).length;
// 第一種方式有兼容性問題, 在使用到了JSX的時候兼容性不是很好,推薦使用as來進行類型轉(zhuǎn)換(類型斷言)

接口類型

和number、string、boolean、enum這些數(shù)據(jù)類型一樣,接口也是一種類型,也是用來約束使用者的。

// 定義一個接口類型
interface FullName{
    firstName:string
    lastName:string
}
let obj = {
    firstName:'Jonathan',
    lastName:'Lee'
    // lastName:18
};
function say({firstName, lastName}:FullName):void {
    console.log(`我的姓名是:${firstName}_${lastName}`);
}
say(obj);

注意點:如果使用接口來限定了變量或者形參,那么在給變量或者形參賦值的時候,賦予的值就必須和接口限定的一模一樣才可以,多一個或者少一個都不行。

可選屬性

參數(shù)傳遞少一個或多個可以使用可選屬性 ?

// 定義一個接口
interface FullName{
    firstName:string
    lastName:string
    middleName?:string  // ?: 可選屬性,使用接口的函數(shù),參數(shù)可傳可不傳
    [propName:string]:any
}
function say({firstName, lastName, middleName}:FullName):void {...}

參數(shù)傳遞多一個或者多多個

// 方式一: 使用類型斷言
say({firstName:'Jonathan', lastName:'Lee', middleName:"666", abc:'abc'} as FullName);
// 方式二: 使用變量  不推薦
let obj = {firstName:'Jonathan', lastName:'Lee', middleName:"666", abc:'abc'};
say(obj);
// 方式三: 使用索引簽名
interface FullName{
    firstName:string
    lastName:string
    middleName?:string
    [propName:string]:any // 表示 key 為 string類型,值any類型
}
function say({firstName, lastName, middleName}:FullName):void {...}
say({firstName:'Jonathan', lastName:'Lee', middleName:"666", abc:'abc', 123:123, def:"def"});
// 對象中key默認(rèn)會給你轉(zhuǎn)成字符串

索引簽名

索引簽名用于描述那些“通過索引得到”的類型,比如arr[10]obj["key"]

interface FullName {
    [propName:string]:string
}
let obj:FullName = { // 注意點: 只要key和value滿足索引簽名的限定即可, 無論有多少個都無所謂
    firstName:'Jonathan',
    lastName:'Lee',
    // middleName:false // 報錯
    // false: '666' // 無論key是什么類型最終都會自動轉(zhuǎn)換成字符串類型, 所以沒有報錯
}
interface stringArray {
    [propName:number]:string
}
let arr:stringArray = {
    0:'a',
    1:'b'
};

只讀屬性

readonly 讓對象屬性只能在對象剛剛創(chuàng)建的時候修改其值。

interface FullName {
    firstName:string
    readonly lastName:string
}
let myName:FullName = {
    firstName: 'Jonathan',
    lastName: 'Lee'
};
myName.lastName = 'Wang'; // 報錯

只讀數(shù)組

ReadonlyArray

let arr2:ReadonlyArray<string> = ['a', 'b', 'c'];

函數(shù)接口

可以使用接口來限定函數(shù)

interface SumInterface {
    (a:number, b:number):number
}
let sum:SumInterface = function (x:number, y:number):number {
    return x + y;
}
let res = sum(10, 20); // 30

混合類型接口

約定的內(nèi)容中既有對象屬性,又有函數(shù)。

// 定義一個函數(shù)實現(xiàn)變量累加
let demo = (()=>{ // 使用閉包確實可以解決污染全局空間的問題, 但是不太友好
    let count = 0;
    return ()=>{
        count++;
        console.log(count);
    }
})();
demo();
demo();
// 在JS中函數(shù)的本質(zhì)就是一個對象
let demo = function () {
    demo.count++; // 函數(shù)對象里面還有屬性
}
demo.count = 0;
demo();
demo();
// ts寫法:混合類型接口
// CountInterface 接口要求數(shù)據(jù)既要是一個沒有參數(shù)沒有返回值的函數(shù),又要是一個擁有count屬性的對象;fn作為函數(shù)的時候符合接口中函數(shù)接口的限定 ():void,fn作為對象的時候符合接口中對象屬性的限定  count:number
interface CountInterface {
    ():void
    count:number
}
let getCounter = function ():CountInterface {
    let fn = <CountInterface>function () { // 使用類型斷言,避免fn.count++報錯
        fn.count++;
        console.log(fn.count);
    }
    fn.count = 0;
    return fn;
};
getCounter();
getCounter();

接口的繼承

TS中的接口和JS中的類一樣是可以繼承的。

interface WidthInterface {
    number
}
interface HeightInterface {
    height:number
}
interface RectInterface extends WidthInterface,HeightInterface {
    // number
    // height:number
    color:string
}
let rect:RectInterface = {
    20,
    height:30,
    color:'red'
}

函數(shù)

TS中的函數(shù)大部分和JS相同;

命名函數(shù)

function say1(name) {
    console.log(name);
}
// ts
function say1(name:string):void {
    console.log(name);
}

匿名函數(shù)

let say2 = function (name) {
    console.log(name);
}
let say2 = function (name:string):void {
    console.log(name);
}

箭頭函數(shù)

let say3 = (name) => {
    console.log(name);
}
let say3 = (name:string):void =>{
    console.log(name);
}

函數(shù)聲明和重載

在TS中函數(shù)的完整格式應(yīng)該是由函數(shù)的定義和實現(xiàn)兩個部分組成的。

函數(shù)聲明

// js定義一個函數(shù)
let AddFun:(a:number, b:number)=>number;
// 根據(jù)定義實現(xiàn)函數(shù)
AddFun = function (x:number, y:number):number {
    return x + y;
};
let res = AddFun(10, 20);

// 一步到位寫法
let AddFun:(a:number, b:number)=>number =
function (x:number, y:number):number {
    return x + y;
};

// 根據(jù)函數(shù)的定義自動推導(dǎo)對應(yīng)的數(shù)據(jù)類型
let AddFun:(a:number, b:number)=>number =
    function (x, y) {
        return x + y;
    };

/////////////////////  TS函數(shù)聲明  /////////////////////
// 先聲明一個函數(shù)
type AddFun = (a:number, b:number)=>number;
// 再根據(jù)聲明去實現(xiàn)這個函數(shù)
let add:AddFun = function (x:number, y:number):number {
    return x + y;
};
let add:AddFun = function (x, y) {
    return x + y;
};

函數(shù)重載

/////////////////////  TS函數(shù)重載  /////////////////////
function getArray(x:number):number[] {
    let arr = [];
    for(let i = 0; i <= x; i++){
        arr.push(i);
    }
    return arr;
}
function getArray(str:string):string[] {
    return str.split('');
}

可選參數(shù)

function add(x:number, y:number, z?:number):number {
    return x + y + (z ? z : 0);
}
let res = add(10, 20);
let res = add(10, 20, 30);

可選參數(shù)可以配置函數(shù)重載一起使用,這樣可以讓函數(shù)重載變得更加強大。

function add(x:number, y:number):number; // 函數(shù)定義
function add(x:number, y:number, z:number):number; // 函數(shù)定義
function add(x:number, y:number, z?:number) { // 函數(shù)實現(xiàn),可選參數(shù)可以多個,可選參數(shù)后面的參數(shù)也必須是可選參數(shù)
    return x + y + (z ? z : 0);
}
let res = add(10, 20, 30);

默認(rèn)參數(shù)

function add(x:number, y:number=10):number {
    return x + y;
}
let res = add(10); // 20

剩余參數(shù)

function add(x:number, ...ags:number[]) {
    console.log(x);
    console.log(ags);
}
add(10, 20, 30, 40, 50) // 10 20 30 40 50

泛型

在編寫代碼的時候我們既要考慮代碼的健壯性,又要考慮代碼的靈活性和可重用性,通過TS的靜態(tài)檢測能讓我們編寫的代碼變得更加健壯,但是在變得健壯的同時卻丟失了靈活性和可重用性,所以為了解決這個問題TS推出了泛型的概念。通過泛型不僅可以讓我們的代碼變得更加健壯,還能讓我們的代碼在變得健壯的同時保持靈活性和可重用性。

let getArray = (value:any, items:number = 5):number[]=>{ // 失去ts優(yōu)勢
    return new Array(items).fill(value);
};
let getArray = <T>(value:T, items:number = 5):T[]=>{
    return new Array(items).fill(value);
};
let arr = getArray<string>('abc');
// 泛型具體的類型可以不指定,如果沒有指定, 那么就會根據(jù)我們傳遞的泛型參數(shù)自動推導(dǎo)出來
let arr = getArray(6);

泛型約束

默認(rèn)情況下我們可以指定泛型為任意類型,但是有些情況下我們需要指定的類型滿足某些條件后才能指定,那么這個時候我們就可以使用泛型約束。

interface LengthInterface{
    length:number
}
let getArray = <T extends LengthInterface>(value:T, items:number = 5):T[]=>{
    return new Array(items).fill(value);
};
let arr = getArray<string>('abc');
// let arr = getArray<number>(6); // 報錯

在泛型約束中使用類型參數(shù)

一個泛型被另一個泛型約束,就叫做泛型約束中使用類型參數(shù)。

let getProps = <T, K extends keyof T>(obj:T, key:K):any=>{ // K必須是T中key
    return obj[key];
}
let obj = {
    a:'a',
}
let res = getProps(obj, "a");
// let res = getProps(obj, "c"); // 報錯,c不是obj中存在的key.

class Person {
    name:string; // 和ES6區(qū)別, 需要先定義實例屬性, 才能夠使用實例屬性
    age:number;
    constructor(name:string, age:number){
        this.name = name;
        this.age = age;
    }
    say():void{
        console.log(`我的名稱叫${this.name}, 我的年齡是${this.age}`);
    }
    static food:string; // 靜態(tài)屬性
    static eat():void{ // 靜態(tài)方法
        console.log(`我正在吃${this.food}`);
    }
}
////////////////////////////////////
let p = new Person('lnj', 34);
p.say();
Person.food = '蛋撻';
Person.eat();
////////////////////////////////////
class Student extends Person{
    book:string;
    constructor(name:string, age:number, book:string){
        super(name, age);
        this.book = book;
    }
    say():void{
        console.log(`我是重寫之后的say-${this.name}${this.age}${this.book}`);
    }
    static eat():void{
        console.log(`我是重寫之后的eat-${this.food}`);
    }
}
let stu = new Student('zs', 18, '從零玩轉(zhuǎn)');
stu.say(); // 我是重寫之后的say-zs18從零玩轉(zhuǎn)
Student.food = '冰淇淋'; // 我是重寫之后的eat-蛋撻
Student.eat();

類屬性和方法修飾符

public(公開的):
如果使用public來修飾屬性, 那么表示這個屬性是公開的; 可以在類的內(nèi)部使用, 也可以在子類中使用, 也可以在外部使用

protected(受保護的):
如果使用protected來修飾屬性, 那么表示這個屬性是受保護的; 可以在類的內(nèi)部使用, 也可以在子類中使用

private(私有的):
如果使用private來修飾屬性, 那么表示這個屬性是私有的; 可以在類的內(nèi)部使用

readonly(只讀的): 創(chuàng)建類初始化后不許修改

構(gòu)造函數(shù)protected后,該類不能通過new創(chuàng)建實例,但是可以被繼承。

可選屬性

在TS中如果定義了實例屬性, 那么就必須在構(gòu)造函數(shù)中初始化,否則就會報錯。如果想不初始化,就需要設(shè)置為可選屬性。和接口中的可選屬性一樣,可傳可不傳的屬性。

class Person {
    name:string;
    age?:number; // 可選屬性
    constructor(name:string, age?:number){
        this.name = name;
        this.age = age;
    }
}
let p = new Person('lnj');

參數(shù)屬性

搞定實例屬性的接收和定義,主要用于簡化代碼。

class Person {
    constructor(public name:string, public age:number){ // 不加public,new Person('meihao', 18); 得到的對象為空 
    }
}
let p = new Person('meihao', 18); // p打印出{name: 'meihao', age: 18}
/////////////////////  不使用參數(shù)屬性  /////////////////////
class Person {
    name:string;
    age:number;
    constructor(name:string, age:number){
        this.name = name;
        this.age = age;
    }
}

類存取器

通過getters/setters來截取對對象成員的訪問。

class Person {
    private _age:number = 0;
    set age(val:number){
        console.log('進入了set age方法');
        if(val < 0){
            throw new Error('人的年齡不能小于零');
        }
        this._age = val;
    }
    get age():number{
        console.log('進入了get age方法');
        return this._age;
    }
}
let p = new Person();
p.age = 34; // 進入了set age方法
// p.age = -6; // 進入了set age方法 人的年齡不能小于零
console.log(p.age);

抽象類

抽象類是專門用于定義哪些不希望被外界直接創(chuàng)建的類的,抽象類一般用于定義基類,抽象類和接口一樣用于約束子類。

抽象類和接口區(qū)別:接口中只能定義約束,不能定義具體實現(xiàn),而抽象類中既可以定義約束,又可以定義具體實現(xiàn)。

abstract class Person {
    abstract name:string;
    abstract say():void;
    eat():void{
        console.log(`${this.name}正在吃東西`);
    }
}
class Student extends Person{
    name:string = 'test';
    say():void{
        console.log(`我的名字是${this.name}`);
    }
}
let stu = new Student();
stu.say(); // 我的名字是test
stu.eat(); // test正在吃東西

類和接口

類"實現(xiàn)"接口,只要實現(xiàn)的某一個接口,那么就必須實現(xiàn)接口中所有的屬性和方法。

interface PersonInterface {
    name:string;
    say():void;
}
class Person implements PersonInterface{
    name:string = 'test';
    say():void{
        console.log(`我的名字叫:${this.name}`);
    }
}
let p = new Person();
p.say();

接口"繼承"類,只要一個接口繼承了某個類,那么就會繼承這個類中所有的屬性和方法,但是只會繼承屬性和方法的聲明,不會繼承屬性和方法實現(xiàn);如果接口繼承的類中包含了protected的屬性和方法,那么就只有這個類的子類才能實現(xiàn)這個接口;

class Person {
    protected name:string = 'test'; // 
    // name:string = 'test';
    age:number = 34;
    protected say():void{
        console.log(`name = ${this.name}, age = ${this.age}`);
    }
}
// let p = new Person();
// p.say();
interface PersonInterface extends Person{
    gender:string;
}
// 接口繼承的類中包含了protected的屬性和方法, 那么就只有這個類的子類才能實現(xiàn)這個接口
class Student extends Person implements PersonInterface{ // 所以Person中有受保護方法和屬性,這里要extends Person
    gender:string = 'male';
    name:string = 'zs';
    age:number = 18;
    say():void{
        console.log(`name = ${this.name}, age = ${this.age}, gender = ${this.gender}`);
    }
}

類和泛型

// 泛型類
class Chache<T> {
    arr:T[] = [];
    add(value:T):T{
        this.arr.push(value);
        return value;
    }
    all():T[]{
        return this.arr;
    }
}
let chache = new Chache<number>();
chache.add(1);
chache.add(3);
console.log(chache.all()); // [1, 3]

接口合并現(xiàn)象

當(dāng)我們定義了多個同名的接口時,多個接口的內(nèi)容會自動合并。

interface TestInterface {
    name:string;
}
interface TestInterface {
    age:number;
}
class Person implements TestInterface{ // 接口合并,必須同時實現(xiàn)name,age
    age:number = 19;
    name:string = 'test';
}

自動類型推斷

不用明確告訴編譯器具體是什么類型,編譯器就知道是什么類型。

////////////////   根據(jù)初始化值自動推斷   ////////////////
// 如果是先定義在初始化, 那么是無法自動推斷的
let value;
value = 123;
value = false;
// 如果是定義的同時初始化, 那么TS就會自動進行類型推薦
let value = 123; // let value:number = 123;
value = 456;
value = false;  // 報錯

////////////////   根據(jù)上下文類型自動推斷   ////////////////
window.onmousedown = (event)=>{ // window.onmousedown = (event:MouseEvent)=>
    console.log(event.target);
}

類型兼容性

interface TestInterface {
    name:string;
    children:{
        age:number
    };
}

let p1 = {name:'zs', children:{age:18}};
let p2 = {name:'zs',children:{age:'abc'},gender: 'box'};
let t:TestInterface;
t = p1; // 會遞歸檢查
t = p2; // 可多不可少,兼容

函數(shù)兼容性

參數(shù)

////////////////   參數(shù)個數(shù)   ////////////////
let fn1 = (x:number, y:number)=>{};
let fn2 = (x:number)=>{};
fn1 = fn2;
fn2 = fn1; // 可少不可多

////////////////   參數(shù)類型,返回值類型   ////////////////
// 必須一模一樣

函數(shù)雙向協(xié)變

////////////////   函數(shù)雙向協(xié)變   ////////////////
// 參數(shù)的雙向協(xié)變
let fn1 = (x:(number | string)) =>{};
let fn2 = (x:number) =>{};
fn1 = fn2;
fn2 = fn1;
// 返回值雙向協(xié)變
let fn1 = (x:boolean):(number | string) => x ? 123 : 'abc';
let fn2 = (x:boolean):number => 456;
fn1 = fn2; // 可以將返回值是具體類型的賦值給聯(lián)合類型
fn2 = fn1; // 不能將返回值是聯(lián)合類型的賦值給具體類型

函數(shù)重載

function add(x:number, y:number):number;
function add(x:string, y:string):string;
function add(x, y) {
    return x + y;
}

function sub(x:number, y:number):number;
function sub(x, y) {
    return x - y;
}
// let fn = add;
// fn = sub; // 不能將重載少的賦值給重載多的

let fn = sub;
fn = add; // 可以將重載多的賦值給重載少

枚舉兼容性

數(shù)字枚舉與數(shù)值兼容,數(shù)字枚舉與數(shù)字枚舉不兼容,字符串枚舉與字符串不兼容

enum Gender{
    Male,
    Female
}
let value:Gender;
value = Gender.Male;
value = 1 // 數(shù)字枚舉與數(shù)值兼容

類兼容性

只比較實例成員,不比較類的構(gòu)造函數(shù)和靜態(tài)成員。

class Person {
    public name:string;
    public age:number;
    // public static age:number; // 如果name改成靜態(tài)成員,下面 p = a不報錯
    constructor(name:string, age:number){}
}
class Animal {
    public name:string;
    constructor(name:string){}
}
let p: Person;
let a: Animal;
// p = a; // 報錯
a = p; // 可多不少

類的私有屬性和受保護屬性會響應(yīng)兼容性。

class Person {
    protected name:string;
}
class Animal {
    protected name:string;
}
let p: Person;
let a: Animal;
// p = a; // 報錯
// a = p; // 報錯

泛型兼容性

泛型只影響使用的部分,不會影響聲明的部分。

interface TestInterface<T> {
    // age:T; // 不加這個下面 t1 t2賦值不報錯,加了就報錯。加了下面t1 t2聲明有具體的類型,賦值就報錯
}
let t1: TestInterface<number>; // age:number
let t2: TestInterface<string>; // age:string
t1 = t2;
t2 = t1;

交叉和聯(lián)合類型

交叉類型

格式:type1 & type2 & ... 交叉類型是將多個類型合并為一個類型

let mergeFn = <T, U>(arg1:T, arg2:U):(T & U)=>{ // (T & U) 表示T和U類型合并后的類型
    let res = {} as (T & U);
    res = Object.assign(arg1, arg2);
    return res;
};
let res = mergeFn({name:'test'}, {age:18}); 
console.log(res); // {name: 'test', age:18}

聯(lián)合類型

格式:type1 | type2 | ... 聯(lián)合類型是多個類型中的任意一個類型

let value: (string | number);
value = 'abc';
value = 123;

類型保護

對于聯(lián)合類型的變量,在使用時如何確切告訴編譯器它是哪一種類型,通過類型斷言或者類型保護。

let getRandomValue = ():(string | number)=>{
    let num = Math.random();
    return (num >= 0.5) ? 'abc' : 123.123;
}
let value = getRandomValue();
if((value as string).length){
    console.log((value as string).length);
}else{
    console.log((value as number).toFixed());
} 
// 優(yōu)化: 定義了一個類型保護函數(shù), 這個函數(shù)的'返回類型'是一個布爾類型
// 這個函數(shù)的返回值類型是: (傳入的參數(shù) is 具體類型)
function isString(value:(string | number)): value is string {
    return typeof value === 'string'; // true,value is string 為true. false, value is string 為false
}
if(isString(value)){
    console.log(value.length);
}else{
    console.log(value.toFixed());
}

null和undefined

TypeScript具有兩種特殊的類型, null和 undefined,它們分別具有值null和undefined。默認(rèn)情況下我們可以將 null和 undefined賦值給任意類型,默認(rèn)情況下null和 undefined也可以相互賦值。

如果不想把 null和 undefined賦值給其它的類型,或者不想讓 null和 undefined相互賦值,那么可以修改tsconfig.json配置,開啟strictNullChecks

如果我們開啟了strictNullChecks,還想把null和 undefined賦值給其它的類型,就必須在聲明的時候使用聯(lián)合類型。

let value:(number | null | undefined);

對于可選屬性和可選參數(shù)而言,如果開啟了strictNullChecks,那么默認(rèn)情況下數(shù)據(jù)類型就是聯(lián)合類型。

class Person {
    name?:string
}
function say(age?:number) {}

null或 undefined檢測

!
function getLength(value:(string | null | undefined)) {
    value = 'abc';
    return ()=>{
        // return value.length; // 報錯
        // return (value || '').length; // 解決報錯的第一種方式
        // return (value as string).length; // 解決報錯的第二種方式
        // 我們可以使用!來去除null和undefined
        // !的含義就是這個變量一定不是null和undefined,一定有值的
        return value!.length;
    }
}
let fn = getLength('www');
let res = fn();
console.log(res);

類型別名

類型別名就是給一個類型起個新名字,但是它們都代表同一個類型。

// 給string類型起了一個別名叫做MyString, 那么將來無論是MyString還是string都表示string
type MyString = string;
////////////////  類型別名也可以使用泛型  ////////////////
// type MyType<T> = {x:T, y:T};
// let value:MyType<number>;
value = {x:123, y:456};
////////////////  接口和類型別名相互兼容  ////////////////
type MyType = {
    name:string
}
interface MyInterface {
    name:string
}
let value1:MyType = {name:'lnj'};
let value2:MyInterface = {name:'zs'};
value1 = value2;
value2 = value1;

類型別名和接口

相同點:

都可以描述屬性或方法
都允許拓展

不同點:

type 可以聲明基本類型別名,聯(lián)合類型,元組等類型, interface不能
type不會自動合并


字面量類型

字面量就是源代碼中一個固定的值
例如數(shù)值字面量: 1,2,3,...
例如字符串字面量: 'a','abc',...
////////////////  TS中我們可以把字面量作為具體的類型來使用  ////////////////
當(dāng)使用字面量作為具體類型時, 該類型的取值就必須是該字面量的值
type MyNum = 1;
let value1:MyNum = 1;
let value2:MyNum = 2; // 報錯

可辨識聯(lián)合

可辨識聯(lián)合具有共同的可辨識特征。一個類型別名,包含了具有共同的可辨識特征的類型的聯(lián)合。可辨識聯(lián)合可以在編譯的時候推斷出當(dāng)前類型。

interface Square {
    kind: "square"; // 共同的可辨識特征
    size: number;
}
interface Rectangle {
    kind: "rectangle"; // 共同的可辨識特征
     number;
    height: number;
}
interface Circle {
    kind: "circle"; // 共同的可辨識特征
    radius: number;
}
/*
Shape就是一個可辨識聯(lián)合
因為: 它的取值是一個聯(lián)合
因為: 這個聯(lián)合的每一個取值都有一個共同的可辨識特征
* */
type Shape = (Square | Rectangle | Circle); // 可辨識聯(lián)合可以在編譯的時候推斷出當(dāng)前類型
function aera(s: Shape) { // 不需要做類型斷言
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.width * s.height;
        case "circle": return  Math.PI * s.radius ** 2; // **是ES7中推出的冪運算符
    }
}

可辨識聯(lián)合完整性檢查

方式一: 給函數(shù)添加返回值 并 開啟strictNullChecks(使用這個方式,上面的例子三種類型的case語句都需要有)

方式二: 添加default + never (不推薦)

function MyNever(x: never):never { // 類型為 never
    throw new Error('可辨識聯(lián)合處理不完整' + x);
}
function aera(s: Shape):number{
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.width * s.height;
        case "circle": return  Math.PI * s.radius ** 2; // **是ES7中推出的冪運算符
        default:return MyNever(s); // 處理的不完整這里會提示報錯
    }
}

索引類型

通過[]索引類型訪問操作符, 我們就能得到某個索引的類型。

class Person {
    name:string;
    age:number;
}
type MyType = Person['name']; // string 類型
// 索引訪問操作符注意點
// 不會返回null/undefined/never類型
interface TestInterface {
    a:string,
    b:number,
    c:boolean,
    d:symbol,
    e:null,
    f:undefined,
    g:never
}
type MyType = TestInterface[keyof TestInterface];
// 獲取指定對象, 部分屬性的值, 放到數(shù)組中返回
let obj = {
    name:'lnj',
    age:18,
    gender:true
}
function getValues<T, K extends keyof T>(obj:T, keys:K[]):T[K][] {
    let arr = [] as T[K][];
    /*
        Person['name'] === string
        Person['age'] === number
        (string | number)[]
    */
    keys.forEach(key=>{
        arr.push(obj[key]);
    })
    return arr;
}
let res = getValues(obj, ['name', 'age']);

映射類型

根據(jù)舊的類型創(chuàng)建出新的類型, 我們稱之為映射類型。


分布式條件類型

條件類型(三目運算)

判斷前面一個類型是否是后面一個類型或者繼承于后面一個類型;如果是就返回第一個結(jié)果, 如果不是就返回第二個結(jié)果。

語法: T extends U ? X : Y;
type MyType<T> = T extends string ? string : any;
type res = MyType<boolean> // res 就是 any類型

分布式條件類型Exclude

type MyType<T> = T extends any ? T : never;
type res = MyType<string | number | boolean>; // res 就是 (string | number | boolean)
// 應(yīng)用:從T中剔除可以賦值給U的類型。 
type MyType<T, U> = T extends U ? never : T;
type res = MyType<string | number | boolean, number> // res = (string | boolean)

ts已經(jīng)封裝好了Exclude,分布式條件類型

type res = Exclude<string | number | boolean, number>
///// 相反操作,提取可以賦值給U的。
type res = Extract<string | number | boolean, number | string> // 提取可以賦值給number和string的類型,聯(lián)合類型、一個類型都可以。
///// 從T中剔除null和undefined。 NonNullable。
type res = NonNullable<string | null | boolean | undefined>
///// 獲取函數(shù)返回值類型。 ReturnType
type res = ReturnType<(()=>number)> // number 類型
///// 獲取一個類的構(gòu)造函數(shù)參數(shù)組成的元組類型。 ConstructorParameters
class Person {
    constructor(name:string, age:number){}
}
type res = ConstructorParameters<typeof Person>; // res = [string, number]
///// 獲得函數(shù)的參數(shù)類型組成的元組類型。 Parameters
function say(name:string, age:number, gender:boolean) {}
type res = Parameters<typeof say>; // res = [string, number, boolean]

infer關(guān)鍵字

條件類型提供了一個infer關(guān)鍵字, 可以讓我們在條件類型中定義新的類型。

// 需求: 定義一個類型, 如果傳入的是數(shù)組, 就返回數(shù)組的元素類型, 如果傳入的是普通類型, 則直接返回這個類型。
type MyType<T> = T extends any[] ? T[number] : T;
type res = MyType<string[]>; // string[]
type res = MyType<number>; // number

type MyType<T> = T extends Array<infer U> ? U : T;
type res = MyType<string[]>; // string[]
type res = MyType<number>; // number

unknown

unknown類型是TS3.0中新增的一個頂級類型,被稱作安全的any

1.任何類型都可以賦值給unknown類型
let value:unknown;   value = 123;    value = "abc";    value = false;

2.如果沒有類型斷言或基于控制流的類型細化, 那么不能將unknown類型賦值給其它類型
let value1:unknown = 123;
let value2:number;
value2 = value1; // 報錯
value2 = value1 as number; // 不報錯
if(typeof value1 === 'number'){ // 類型細化
    value2 = value1;
}

3.如果沒有類型斷言或基于控制流的類型細化, 那么不能在unknown類型上進行任何操作
let value1:unknown = 123;
value1++; // 報錯
(value1 as number)++;

4.只能對unknown類型進行 相等或不等操作, 不能進行其它操作(因為其他操作沒有意義)
let value1:unknown = 123;
let value2:unknown = 123;
console.log(value1 === value2);
console.log(value1 !== value2);
console.log(value1 >= value2); // 雖然沒有報錯, 但是不推薦, 如果想報錯提示, 可以打開嚴(yán)格模式

5.unknown與其它任何類型組成的交叉類型最后都是其它類型
type MyType = number & unknown; // number
type MyType = unknown & string;

6.unknown除了與any以外, 與其它任何類型組成的聯(lián)合類型最后都是unknown類型
type MyType = unknown | any; // any
type MyType = unknown | number;
type MyType = unknown | string | boolean;

7.never類型是unknown類型的子類型
type MyType = never extends unknown ? true : false; // true

8.keyof unknown等于never
type MyType = keyof unknown;

9.unknown類型的值不能訪問其屬性,方法,創(chuàng)建實例
class Person {
    name:string = 'lnj';
    say():void{
        console.log(`name = ${this.name}`);
    }
}
let p:unknown = new Person(); // 設(shè)置為unknown類型,下面的方法報錯
p.say();
console.log(p.name);

10.使用映射類型時, 如果遍歷的是unknown類型, 那么不會映射任何屬性
type MyType<T> = {
    [P in keyof T]:any
}
type res = MyType<unknown> // 空對象

Symbols

和ES6中的Symbol一樣。


迭代器和生成器

迭代器for...of Iterator接口

let someArray = [1, "string", false];
for (let entry of someArray) {
    console.log(entry); // 1, "string", false
}

生成器

當(dāng)生成目標(biāo)為ES5或ES3,迭代器只允許在Array類型上使用。在非數(shù)組值上使用 for..of語句會得到一個錯誤,就算這些非數(shù)組值已經(jīng)實現(xiàn)了Symbol.iterator屬性。為了解決這個問題, 編譯器會生成一個簡單的for循環(huán)做為for..of循環(huán)


模塊系統(tǒng)

TS中的模塊幾乎和ES6和Node中的模塊一致

1.ES6模塊
1.1分開導(dǎo)入導(dǎo)出
export xxx;
import {xxx} from "path";

1.2一次性導(dǎo)入導(dǎo)出
export {xxx, yyy, zzz};
import {xxx, yyy, zzz} from "path";

1.3默認(rèn)導(dǎo)入導(dǎo)出
export default xxx;
import xxx from "path";
//////////////////////////////////////////////////////// 
2.Node模塊
1.1通過exports.xxx = xxx導(dǎo)出
通過const xxx = require("path");導(dǎo)入
通過const {xx, xx} = require("path");導(dǎo)入

1.2通過module.exports.xxx = xxx導(dǎo)出
通過const xxx = require("path");導(dǎo)入
通過const {xx, xx} = require("path");導(dǎo)入

ES6的模塊和Node的模塊是不兼容的, 所以TS為了兼容兩者就推出了
export = xxx;
import xxx = require('path');

命名空間

命名空間可以看做是一個微型模塊,當(dāng)我們先把相關(guān)的業(yè)務(wù)代碼寫在一起,又不想污染全局空間的時候,我們就可以使用命名空間,本質(zhì)就是定義一個大對象,把變量/方法/類/接口...的都放里面。

命名空間和模塊區(qū)別:

在程序內(nèi)部使用的代碼,可以使用命名空間封裝和防止全局污染;在程序內(nèi)部外部使用的代碼,可以使用模塊封裝和防止全局污染;

**總結(jié): **由于模塊也能實現(xiàn)相同的功能,所以大部分情況下用模塊即可。

namespace Validation {
    const lettersRegexp = /^[A-Za-z]+$/;
    export const LettersValidator  = (value) =>{ // export 暴露給外界訪問
        return lettersRegexp.test(value);
    }
}
const lettersRegexp = /^[A-Za-z]+$/; // 再定義一個相同的,不會報錯
console.log(Validation.LettersValidator('abc'));
  
可以將命名空間放到單獨的ts, 用下面的方式引入,引入前需要對這個ts打包:tsc --outFile ./tests.ts
/// <reference path="./test.ts" /> 
// 使用前需要打包,tsc --outFile ./tests.ts

聲明合并

接口:在ts當(dāng)中接口和命名空間是可以重名的,ts會將多個同名的合并為一個;同名接口如果屬性名相同, 那么屬性類型必須一致;同名接口如果出現(xiàn)同名函數(shù), 那么就會成為一個函數(shù)的重載;

命名空間:同名的命名空間中不能出現(xiàn)同名的變量、方法等;同名的命名空間中其它命名空間沒有通過export導(dǎo)出的內(nèi)容是獲取不到的;

命名空間和同名的類合并: 命名空間和類合并前提,類必須定義在命名空間的前面;會將命名空間中導(dǎo)出的方法作為一個靜態(tài)方法合并到類中;

class Person {
    say():void{
        console.log('hello world');
    }
}
namespace Person{
    export const hi = ():void=>{
        console.log('hi');
    }
}

命名空間和函數(shù)合并:函數(shù)必須定義在命名空間的前面;

function getCounter() {
    getCounter.count++;
    console.log(getCounter.count);
}
namespace getCounter{
    export let count:number = 0;
}

命名空間和枚舉合并:沒有先后順序的要求;

enum Gender {
    Male,
    Female
}
namespace Gender{
    export const Yao:number = 666;
}

裝飾器

Decorator 是 ES7 的一個新語法,目前仍處于提案中。裝飾器是一種特殊類型的聲明,它能夠被附加到類,方法, 訪問器,屬性或參數(shù)上;添加到不同地方的裝飾器有不同的名稱和特點。

+ 附加到類上, 類裝飾器
+ 附加到方法上,方法裝飾器
+ 附加到訪問器上,訪問器裝飾器
+ 附加到屬性上,屬性裝飾器
+ 附加到參數(shù)上,參數(shù)裝飾器

在TS中裝飾器也是一項實驗性的特性,所以要使用裝飾器需要手動打開相關(guān)配置修改配置文件experimentalDecorator

裝飾器基本格式:

普通裝飾器

// 給Person這個類綁定了一個普通的裝飾器,裝飾器的代碼會在定義類之前執(zhí)行, 并且在執(zhí)行的時候會把這個類傳遞給裝飾器
function test(target) {
    console.log('test');
}
@test
class Person {} // test

裝飾器工廠

// 如果一個函數(shù)返回一個回調(diào)函數(shù), 如果這個函數(shù)作為裝飾器來使用, 那么這個函數(shù)就是裝飾器工廠
function test(target) {
    console.log('test');
}
function demo() {
    console.log('demo out');
    return (target)=>{
        console.log('demo in');
    }
}
@test()  // 給Person這個類綁定了一個裝飾器工廠,在綁定的時候由于在函數(shù)后面寫上了(),所以會先執(zhí)行裝飾器工廠拿到真正的裝飾器,真正的裝飾器會在定義類之前執(zhí)行, 所以緊接著又執(zhí)行了里面
class Person {} // demo out   demo in

裝飾器組合

普通的裝飾器可以和裝飾器工廠結(jié)合起來一起使用,結(jié)合起來一起使用的時候, 會先從上至下的執(zhí)行所有的裝飾器工廠, 拿到所有真正的裝飾器,然后再從下至上的執(zhí)行所有的裝飾器。

@test
@demo()
class Person {} // demo out   demo in   test

類裝飾器

類裝飾器在類聲明之前綁定(緊靠著類聲明)
類裝飾器可以用來監(jiān)視,修改或替換類定義
在執(zhí)行類裝飾器函數(shù)的時候, 會把綁定的類作為其唯一的參數(shù)傳遞給裝飾器
如果類裝飾器返回一個新的類,它會新的類來替換原有類的定義

function test(target:any) {
    target.prototype.personName = 'hello';
    target.prototype.say = ():void=>{ // say方法實現(xiàn)
        console.log(`my name is ${target.prototype.personName}`);
    }
}
@test
class Person {}
interface Person{ // 和Person類合并,有了say方法的聲明,下面p.say()就不會合并,
    say():void;
}
let p = new Person();
p.say(); // 報錯, my name is hello
// test接收的參數(shù){ {構(gòu)造函數(shù)} }
function test<T extends {new (...args:any[]):{}}>(target:T) {
    return class extends target { // 返回一個類
        name:string = 'lnj';
        age:number = 18;
    }
}
@test  // 返回的類會替換person
class Person {}
let p = new Person();
console.log(p); // {name: 'hello', age: 18}

defineProperty

可以直接在一個對象上定義一個新屬性,或者修改一個對象的現(xiàn)有屬性,并返回此對象。

// 定義一個新的屬性
let obj = {age:18};
Object.defineProperty(obj, 'name', {
    value:'lnj'
});
// 修改原有屬性
let obj = {age:18};
Object.defineProperty(obj, 'age', {
    value:34
});
// 修改屬性配置-讀寫
let obj = {age:18};
Object.defineProperty(obj, 'age', {
    writable:false
})
obj.age = 34; // 報錯
// 修改屬性配置-迭代
let obj = {age:18, name:'lnj'};
Object.defineProperty(obj, 'name', {
    enumerable: false
})
for(let key in obj){
    console.log(key);
}
// // 修改屬性配置-配置
let obj = {age:18, name:'lnj'};
Object.defineProperty(obj, 'name', {
    enumerable:false,
    configurable: false // 一經(jīng)配置,后面再修改都會報錯
});
Object.defineProperty(obj, 'name', {
    enumerable:true,
    configurable: false // 報錯
});
for(let key in obj){
    console.log(key);
}

方法裝飾器

方法裝飾器寫在一個方法的聲明之前(緊靠著方法聲明),法裝飾器可以用來監(jiān)視,修改或者替換方法定義。方法裝飾器表達式會在運行時當(dāng)作函數(shù)被調(diào)用。

傳入下列3個參數(shù):

對于靜態(tài)方法而言就是當(dāng)前的類, 對于實例方法而言就是當(dāng)前的實例
被綁定方法的名字
被綁定方法的屬性描述符

function test(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    // console.log(target);
    // console.log(propertyKey);
    // console.log(descriptor);
}
function test(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.value = ():void=>{ // 通過獲取方法屬性,來替換值,也就是替換方法
        console.log('my name is ls');
    };
}
class Person {
    @test
    sayName():void{
        console.log('my name is zs');
    }
}
let p = new Person();
p.sayName(); // my name is ls

訪問器裝飾器

訪問器裝飾器聲明在一個訪問器的聲明之前(緊靠著訪問器聲明)。訪問器裝飾器應(yīng)用于訪問器的屬性描述符并且可以用來監(jiān)視,修改或替換一個訪問器的定義。

// TypeScript不允許同時裝飾一個成員的get和set訪問器
function test(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.set = (value:string)=>{
        target.myName = value; // target為類的原型對象
    }
    descriptor.get = ():string=>{
        return target.myName;
    }
}
class Person {
    private _name:string;	
    constructor(name:string){
        this._name = name;
    }
    @test
    get name():string{
        return this._name;
    }
    set name(value:string){
        this._name = value;
    }
}
let p = new Person('ls');
p.name = 'zs';
console.log(p.name); // ls
console.log(p.__proto__.myName); // zs

屬性裝飾器

屬性裝飾器寫在一個屬性聲明之前(緊靠著屬性聲明), 屬性裝飾器表達式會在運行時當(dāng)作函數(shù)被調(diào)用,傳入下列2個參數(shù):

對于靜態(tài)屬性來說就是當(dāng)前的類, 對于實例屬性來說就是當(dāng)前實例
成員的名字

function test(target:any, proptyName:string) {
    target[proptyName] = 'test';
}
class Person {
    // @test
    static age:number; // 也可以被裝飾修改
    @test
    name?:string;
}
let p = new Person();
console.log(p.name); // test

參數(shù)裝飾器

參數(shù)裝飾器寫在一個參數(shù)聲明之前(緊靠著參數(shù)聲明)。數(shù)裝飾器表達式會在運行時當(dāng)作函數(shù)被調(diào)用。

傳入下列3個參數(shù):

對于靜態(tài)方法而言就是當(dāng)前的類, 對于實例方法而言就是當(dāng)前的實例
被綁定方法的名字
被綁定方法的屬性描述符

function test(target:any, proptyName:string, index:number) {
    console.log(target);
    console.log(proptyName);
    console.log(index);
}
class Person {
    say(age:number,@test name:string):void{}
}

混入

對象混入

let obj1 = {name:'lnj'};
let obj2 = {age:34};
Object.assign(obj1, obj2);
console.log(obj1);
console.log(obj2); // 無變化

類混入

// 定義兩個類, 將兩個類的內(nèi)容混入到一個新的類中
class Dog {
    name:string = 'wc';
    say():void{
        console.log('wang wang');
    }
}
class Cat {
    age:number = 3;
    run():void{
        console.log('run run');
    }
}
// 一次只能繼承一個類
// class Animal extends Dog, Cat{}
class Animal implements Dog, Cat{
    name:string;
    age:number;
    say:()=>void;
    run:()=>void;
}
function myMixin(target:any, from:any[]) {
    from.forEach((fromItem)=>{ // 類方法混入target
        Object.getOwnPropertyNames(fromItem.prototype).forEach((name)=>{
            target.prototype[name] = fromItem.prototype[name];
        })
    })
}
myMixin(Animal, [Dog, Cat]);
let a = new Animal();
console.log(a);
a.say();
a.run();
// console.log(a.name); // 方法可以混入,屬性不可以
// console.log(a.age);

聲明

在開發(fā)中我們不可避免的需要引用第三方的 JS 的庫,但是默認(rèn)情況下TS是不認(rèn)識我們引入的這些JS庫的,所以在使用這些JS庫的時候,我們就要通過聲明來告訴告TS它是什么,它怎么用。

declare const $:(selector:string)=>{}; // 告訴ts $符號是個類型函數(shù),接收一個string參數(shù)。$是導(dǎo)入的jquery
console.log($); // 有了declare,ts文件中這里就不會報錯

聲明文件

默認(rèn)情況下ts會首先去查找項目中所有js文件和ts文件。也可以去修改ts.config.josn,最后加上include配置,告訴ts去查找哪些文件。

  "include": [
    "./src/**/*.ts",
    "./src/**/*.d.ts",
  ],
// declare.d.ts 文件中寫聲明文件
declare let myName:string;
declare function say(name:string, age:number):void;
// 注意點: 聲明中不能出現(xiàn)實現(xiàn)
declare class Person {
    name:string;
    age:number;
    constructor(name:string, age:number);
    say():void;
}
// test.ts
console.log(myName);
say('lnj', 18);
let p = new Person('zs', 666);
p.say();

對于常用的第三方庫,其實已經(jīng)幫我們編寫好了對應(yīng)的聲明文件。所以在開發(fā)中,如果我們需要使用一些第三方JS庫的時候我們只需要安裝別人寫好的聲明文件即可。

TS聲明文件的規(guī)范 @types/xxx 例如: 想要安裝jQuery的聲明文件,那么只需要npm install @types/jquery 即可。

總結(jié)

以上是生活随笔為你收集整理的TypeScript基本概念的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。