TS基礎

  1. TypeScript是微軟開發的,基于類的面向對象編程,其文件以 .ts 為后綴名;
  2. TypeScript是JavaScript的超集,完全兼容JavaScript代碼;
  3. TypeScript只存活于編譯階段,編譯為JavaScript之后,在瀏覽器/Node環境下才能運行;
  4. TypeScript的安裝與編譯
    npm i -g typescript
    tsc helloworld.ts
    
    1. 默認情況下,編譯生成的js文件輸出到當前目錄下;
    2. tsc helloworld.ts --outDir ./dist:指定編譯后的js文件輸出到dist目錄。

tsconfig.json

tsc --init  //生成TS的配置文件tsconfig.json

{
    "compileOnSave": true,  //自動編譯并保存
    "compilerOptions": {
        "target": "es2017",
        "module": "commonjs",
        "outFile": "./bundle.js",
        "outDir": "./dist",
        "strict": true,
        ......
        "moduleResolution": "node",  //按node編譯
        "removeComments": true,  //生成JS代碼后,移除注釋內容
        "sourceMap": true,  //配合后期調試
        "emitDecoratorMetadata": true,   //支持元數據,裝飾器需要使用
        "typeRoots": []  //為TS編譯器指定檢查類型的依據文件
    },
    "include": [],
    "exclude": []
}
  1. tsconfig.json:用于配置 tsc 的編譯配置選項,如js文件的輸出目錄;
  2. tsc 不指定要編譯的ts文件時,編譯器會從當前目錄開始逐級向上查找tsconfig.json
  3. 當指定了編譯的ts文件時,tsconfig.json會被忽略;
  4. --project(-p):指定一個包含 tsconfig.json 的目錄來進行編譯。

編譯選項:compilerOptions

  1. outDir:編譯生成的js文件的輸出目錄,當前目錄(./)就是tsconfig.json的所在目錄;
  2. outFile:合并輸出到一個JS文件中,合并文件的順序為加載和依賴順序;
  3. module:編譯后的js所使用的模塊化系統,none、commonjs、es2015、amd、esnext ...
  4. target:指定編譯后的js對應的ECMAScript版本,es3、es5、es6、es2015、es2016、es2017 ...

指定要編譯文件

  1. 執行 tsc 命令,但不指定任何ts文件時,默認會編譯當前項目中的所有ts文件;
  2. include:指定要編譯的ts文件目錄
    "include": [ "./src/" ]  //編譯src目錄下的ts文件
    
    1. 使用glob模式,類似于正則表達式,**/ 遞歸匹配任意子目錄;
    2. * 匹配0或多個字符,? 匹配一個任意字符,但都不包括目錄分隔符;
    3. ./src/*:只編譯src目錄下的ts文件,不包括src的子目錄;
    4. ./src/**/*:遞歸編譯src目錄下的ts文件,包括子目錄;
  3. exclude:指定不編譯的ts文件目錄,默認已經排除了 node_modulesoutDir 目錄

數據類型

  1. let 變量: 類型
    let input: HTMLInputElement = document.querySelector('uname');
    let value: number = Number(input.value) + 10;
    let a: string;  a = 1;  //報錯
    
  2. TS的類型:數字、字符串、布爾型、null、undefined、數組、元組、枚舉、void、any、Never
  3. 基本類型與包裝類型
    1. 基本類型:string、number、boolean
    2. 包裝類型:String、Number、Boolean
    3. 基本類型可以直接賦值給對應的包裝類型,但反之不行。
    let s: String = 'nodejs';
    
  4. 布爾類型
    var flag:boolean = true;
    flag = false;
    
  5. 數字類型
    var n:number = 12;
    
  6. 字符串類型
    var s:string = 'hello ts';
    
  7. 數組類型
    1. 基本語法:var arr:number[] = [1, 2, 3]; //元素類型為number的數組
    2. 泛型方式:var arr:Array<number> = [1, 2, 3];
  8. 元組類型:數組的一種,可以存儲多種類型的元素,但 順序不可以顛倒,長度不可以違規,是固定的;
    let tup:[number, string] = [11, 'hello'];
    tup[0] = 20;
    tup[1] = '元組';
    
    v2.6 之前,超出規定個數的元素稱作越界元素,但是只要越界元素的類型是聲明類型中的一種即可,屬于聯合類型,所以沒問題。但 v2.6 之后,要求元組賦值的類型和個數都必須保持一致。
  9. 枚舉類型
    enum 枚舉名{
        標識符[=整型常數],
        標識符[=整型常數],
        標識符[=整型常數],
    }
    
    1. 標識符可以加引號,也可以不加,[] 表示可選參數
    enum Flag {
        success=1,
        error,
        'undefined'=-1
    }
    
    1. 使用枚舉
    let f:Flag = Flag.success;
    console.log(f);  // 1
    
    1. 如果標識符都沒有賦予整型常數,則默認為從 0 開始的角標。
  10. 任意類型
    var num:any = 123;
    num = false;
    
  11. nullundefined,定義了變量,但并未賦值,默認是undefined
    var num:undefined;
    console.log(num);  // undefined,
    var num:null = null;
    console.log(num);  // null
    
  12. 復合類型
    var num:number | undefined | null;  // num 可以是數字類型,也可以是undefined/null
    console.log(num);  // undefined
    num = 100;
    console.log(num);  // 100
    
  13. void類型:表示沒有任何類型,一般用于定義沒有返回值的函數;
    function run():void {
        console.log(123)
    }
    
  14. never類型:表示從不會出現的值,即聲明為never類型的變量只能被never類型所賦值;
    var num:never;
    num = (()=>{
        throw new Error('some happend');  //拋出異常,不屬于任何已知類型
    })();
    
  15. 類型推導
    1. 有時候不一定強制使用類型聲明,TS會根據語境進行類型推導;
    2. TS的變量初始化推導
    let a;  a=1;  //變量a 是number類型,不允許再賦予其他類型的值;
    
    1. TS的上下文推導
    btn.onclick = function(e) {}  //e: MouseEvent
    btn.onkeydown = function(e) {}  //e: KeyboardEvent
    
    1. TS會根據當前綁定的事件,推導出回調函數的第一個參數類型MouseEvent/KeyboardEvent

函數

  1. 函數聲明
    function fn(x: Type, y: Type): Type {
        ...
    }
    
    function fn(x: number, y: number): number {  //參數為number,返回值為number
        return x+y;
    }
    //匿名函數
    let fn = function(x: number, y: number): number {
        return x+y;
    }
    
  2. 函數表達式
    let fn:(x:Type, y:Type) => Type = function(x:Type, y:Type){
        ...
    }
    
    let fn: (x: number, y: number) => number = function(x: number, y: number) {
        return x+y;
    }
    // 函數體function的參數類型可以省略,ts會進行類型推導
    let fn: (x: number, y: number) => number = function(x, y) {
        return x+y;
    }
    
  3. 形參的對象約束
    1. 如果傳入的是匿名對象,其屬性必須與約定屬性一致;
    function print(label:{name:string}):void {
        console.log('print ', label.name);
    }
    print({name:'Machel'});  //print Machel
    
    1. 如果不是匿名對象,則只需要包含約定的屬性即可;
    let obj = {
        name: 'Machel',
        age: 20
    }
    print(obj);  //print Machel
    
  4. 可選參數:與ES6保持一致,必須配置在形參的末尾,且默認值為undefined
    function run(name:string, age?:number):string {
        return 'hello ts';
    }
    run('Jack');
    run('Jack', 20);
    
  5. 默認參數:指定形參的默認值,也必須配置在形參的末尾
    function run(name:string, age:number=20):string {
        return 'hello ts';
    }
    
    1. 如果手動指定了參數的默認值,則不能再聲明為可選參數 ?
    2. 對于默認參數,可以利用TS的自動類型推導,從而不聲明類型。
  6. 剩余參數:三點運算符
    function sum(name:string, ...rest:number[]):number {
        //name是一個必傳參數(字符串類型),其余所有參數(number類型)都被封裝在 rest 數組中
        let sum:number = 0;
        for(let i=0; i<rest.length; i++) {
            sum += rest[i];
        }
        return sum;
    }
    sum('Machel', 1, 2, 3);
    sum('Machel', 1, 2, 3, 4, 5);
    
  7. 函數重載:TS需要兼容ES5和ES6,所以TS的函數重載與Java的函數重載略有不同;
    1. 參數個數相同
    function run(name:string):string;
    function run(age:number):string;
    function run(sn:any):any {
        if(typeof sn === 'string') {  // string
            return 'name is ' + sn;
        } else {  // number
            return 'age is ' + sn;
        }
    }
    run('Jackson'); // name is Jackson
    run(12); // age is 12
    run(true); // 編譯報錯
    
    1. 參數個數不同:借助可選參數
    function run(name:string):string;
    function run(name:string, age:number):string;
    function run(name:any, age?:any):any {
        if(age) {  // age 存在
            return `name: ${name}, age: ${age}`;
        } else {  // age 不存在
            return `name is ${name}`;
        }
    }
    
  8. 函數的this
    1. ts的函數中,this默認指向 any,ts不能對any類型提示任何屬性和方法;
    2. tsconfig.json中,取消 this 默認指向 any 的設置:
    "compilerOptions": {
        "noImplicitThis": true
    }
    
    1. 對于某些情況,如DOM事件,回調函數的this默認指向DOM對象,TS自動推導。

ES5定義類

  1. 構造函數
    function Persion() {
        this.name = 'Machel';
        this.age = 20;
        this.show = function() {
            console.log(this.name, this.age);
        }
    }
    var p = new Persion();
    p.show();  // Machel  20
    
  2. 在原型鏈上擴展屬性和方法
    Persion.prototype.sex = 'male';
    Persion.prototype.work = function() {
        console.log(this.name + 'is work!');
    }
    p.work();
    
  3. 原型鏈上的屬性和方法會被多個實例共享,而構造函數中的屬性和方法只是拷貝一份給每個實例;
  4. 靜態方法
    Persion.run = function() {
        console.log('run');
    }
    Persion.run();
    
  5. 繼承:原型鏈、對象冒充,以及兩種模式的組合
    1. 對象冒充
    function Web() {
        Persion.call(this);  // Web繼承Persion
    }
    var w = new Web();
    w.show();  // Machel  20
    w.work();  // 報錯:對象冒充不能繼承原型鏈上的屬性和方法
    
    1. 原型鏈繼承
    function Web() { }
    Web.prototype = new Persion();
    w.show();  // Machel  20
    //但是這種方式無法給父類傳參
    function Persion(name, age) {
        this.name = name;
        this.age = age;
        this.show = function() {
            console.log(this.name, this.age);
        }
    }
    Web.prototype = new Persion();
    var w = new Web('Machel', 22);
    w.show();  //undefined  undefined,接收不到參數!
    
    1. 組合模式
    function Web(name, age) {
        Persion.call(this, name, age);
    }
    Web.prototype = new Persion();  或 Web.prototype = Persion.prototype;
    
    var w = new Web('Machel', 22);
    w.show();  // Machel  20
    w.work();  // Machel is work!
    
  6. 需要百度,詳細看看ES5中的類

TS的類

  1. TS的類與ES2015(es6)中的 class 類似,同時新增了一些實用特性;
  2. 類的定義
    class Person {
        name:string;  //屬性,默認訪問修飾符為public,默認值為undefined
        constructor(name:string) {
            this.name = name;
        }
        getName():string {
            return this.name;
        }
        run():void {
            console.log(this.name + ' 在 Person');
        }
    }
    var p = new Person('Jackon');
    console.log(p.getName());  // Jackon
    p.run();  // Jackon 在 Person
    
  3. 類的繼承:extends,單繼承
    class Web extends Person {
        constructor(name:string) {
            super(name);  //必須先初始化父類的構造器
        }
        run():void {  //覆寫父類的方法
            console.log(this.name + ' 在 Web');
        }
    }
    var w = new Web('Machel');
    console.log(w.getName());  // Machel
    w.run();  // Machel 在 Web
    
  4. 修飾符:public、protected、private、readonly
    1. public:公有,在類內部、子類、類外部(對象)都可以訪問,默認修飾符
    2. protected:保護類型,在類內部、子類可以訪問,類外部(對象)不能訪問;
    3. private:公有,在類內部可以訪問,子類、類外部(對象)不能訪問;
    4. readonly:只讀,對象只能獲取,不能重新賦值;
    5. 在構造函數的參數上使用修飾符,表示同時在類中創建該屬性,該屬性不能在類中預定義;
    constructor(public age: number){  //為Person創建屬性age:public age: number;
        this.age = age;
    }
    
  5. 存取器:setter/getter的簡寫形式
    private _age: number = 10;
    get age(): number {   //訪問:p1.age;
        return this._age;
    }
    set age(age: number) {   //訪問:p1.age = 20;
        this._age = age;
    }
    
  6. 靜態屬性、方法:static修飾,靜態方法中沒有this,所以只能訪問靜態屬性;
    class Person {
        public name:string|undefined;
        static age:number = 20;
        static print() {
            console.log('print ' + Person.age);
        }
    }
    Person.age;  // 20
    Person.print();  //print 20
    
  7. 多態:繼承的一種表現,父類引用指向子類對象!
    var p:Person = new Web('Machel');
    p.run();  // Machel 在 Web
    

抽象類

abstract:修飾抽象類和抽象方法

  1. 抽象類不能直接實例化,抽象方法不包含具體實現;
  2. 抽象類中可以同時包含抽象方法和普通方法,但抽象方法只能放在抽象類中;
  3. 抽象類的子類如果不是抽象類,則必須實現父類的抽象方法!
    abstract class Animal {
        public name:string;
        constructor(name:string) {
            this.name = name;
        }
        abstract eat():any;
    }
    class Dog extends Animal {
        constructor(name:string) {
            super(name);
        }
        eat() {
            console.log(this.name + ' is eat!')
        }
    }
    var d:Animal  = new Dog('dog');
    d.eat();  // dog is eat!
    

接口:interface

  1. 屬性接口,約束函數的對象形參
    interface Options {
        width: number, height: number
    }
    function print(opts: Options){
        console.log(opts.width, opts.number);
    }
    print({width:100, height:50})
    
    1. TS類型檢測器只會檢查接口所定義的規則屬性是否存在,并不會檢查屬性的順序;
        print({height:50, width:100})
    
    1. 當以匿名對象的方式傳入時,必須嚴格遵守接口規則,不能有多余屬性;
    print({
        firstName: 'Jack',
        secondName: 'Machal',
        age: 1  //編譯報錯:匿名對象的形式傳入時,只能包含接口中約束的屬性
    });
    
    1. 如果傳入的對象不是一個匿名對象,那么只需要包含接口規則的屬性即可;
    var obj = {
        firstName: 'Jack',
        secondName: 'Machal',
        age: 1
    }
    print(obj);  //編譯通過:Jack Machal
    
    1. as 斷言可以繞開TS檢測
    print({ width: 100 } as Options); //編譯通過
    
  2. 可選屬性的接口
    interface FullName {
        firstName:string;
        secondName?:string;  //可選屬性
    }
    print({
        firstName: 'Jack'  //不傳secondName屬性
    })
    
  3. 函數接口
    interface Func {
        (key:string, value:string):string;
    }
    var fn:Func = function(key:string, value:string):string {
        return key + value;
    }
    fn('Jackson', '123456');  //Jackson123456
    
  4. 可索引接口:數組、對象的約束,也就是一組 key-value 的數據,數量是不確定的,其中的key具有某種特性;
    1. key的特性在于:只能是 stringnumber
    2. 數組約束
    interface UserArr {
        [index:number]:string  //索引為number類型,值為string類型
    }
    var arr:UserArr = ['aaa', 'bbb'];
    
    1. 對象約束
    interface UserObj {
        [index:string]:string  //屬性名和屬性值都是string類型
    }
    var obj:UserObj = {name:'Joker', age:'20'}
    
    1. TS類是不允許對象自己直接擴展屬性和方法的,但可以通過接口去擴展!
    class Person {
        name = 'Mack'
    }
    let p = new Person();
    p.run = function(){ }  //編譯報錯!
    
    interface Person {
        [attr: string]: any
    }
    p.fly = function() {  //編譯通過
        console.log('fly: ', this.name);
    }
    p.fly();  // fly: Mack
    
  5. 類類型接口:對類的約束,有點類似于抽象類
    interface Animal {
        name:string;
        eat(foot:string):void;
    }
    class Dog implements Animal {
        name:string;
        constructor(name:string) {
            this.name = name;
        }
        eat() {  //可以只實現接口要求的方法,忽略要求的參數
            console.log('eat...');
        }
    }
    
  6. 接口的繼承
    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('eat...');
        }
        work() {
            console.log('work...');
        }
    }
    
  7. 一個類可以同時實現多個接口,但只能繼承一個類
    class A extends B implements C,D {
    
    }
    

泛型

  1. 泛型變量
    function getData<T>(value:T):T {
        return value;
    }
    getData<number>(123);  //number類型
    getData<string>('Machel');  //string類型
    
    function getData3<T>(value:T):void {
        console.log(value)
    }
    getData<number>(123);  // 123
    
    1. 泛型也可以有多個
    function fn<T, S>(a:T, b:S): [T, S] {
    
    }
    
    1. 還可以是數組形式
    function fn<T>(a: T[]): T[] {
    
    }
    function fn<T>(a: Array<T>): Array<T> {
    
    }
    
  2. 泛型類
    class Min<T> {
        public list:T[] = [];
        add(value:T):void {
            this.list.push(value);
        }
        min():T {
            var m = this.list[0];
            ...
            return m;
        }
    }
    var n = new Min<number>();
    n.add(2);
    
  3. 泛型作為一種類型
    let fn: <T>(x: T, y: T) => number = function(x, y) {
        return Number(x) + Number(y);
    }
    
  4. 泛型接口
    1. 方式一
    interface IFn {
        <T>(x: T): T;
    }
    var fn:IFn = function<T>(x:T):T {
        return x;
    }
    fn<string>('Joker');  //Joker
    
    interface IFn<T> {
        (x: T, y: T): number
    }
    let fn: IFn<string> = function(x, y) {
        return Number(x) + Number(y);
    }
    
    1. 方式二
    interface IFn<T> {
        (x:T):T;
    }
    function fn<T>(x:T):T {
        return x;
    }
    var myGet:IFn<string> = fn;
    myGet('Joker');
    
  5. 泛型約束:extends,約束泛型的類型范圍
    1. 約束泛型為HTML節點對象
    function fn<T extends HTMLElement> (ele: T) {
        
    }
    
    1. 配合接口使用
    interface Len {
        length: number
    }
    function fn<T extends Len> (e: T) {
        
    }
    fn(1);   //編譯報錯:number類型沒有實現 Len 接口,也不具備 length 屬性
    fn('2');  //string類型實現了 Len 接口
    
  6. 類類型
    1. 如何讓一個外部函數成為創建對象的工廠
    function getArray(constructor: Array) {
        return new constructor();
    }
    let arr = getArray(Array);   //編譯報錯
    
    1. 形參constructor表示Array類型的對象,而不是一個Array的構造函數,所以無法創建對象;
    2. {new()}:表示構造函數類型
    function getInstance(constructor: {new()}) {
        return new constructor();
    }
    let arr = getInstance(Array);   //通過TS檢查,創建一個數組對象
    
    1. 限制構造函數的類型:
    function getInstance(ct: {new(): Array<string>}) {
        return new ct();    //只能創建Array<string>類型的對象
    }
    function getInstance<T>(ct: {new(): T}) {  //泛型
        return new ct();
    }
    
  7. 應用:數據庫操作的封裝
    class MySqlDB<T> { //
        add(info:T):boolean {
            //...... 把info中的屬性插入到對應的表中
            return true;
        }
    }
    class User {  //數據庫表的映射對象
        username:string;
        password:string;
        constructor(params:{ username:string, password:string }) {
            this.username = params.username;
            this.password = params.password;
        }
    }
    var u = new User({username:'Joker', password:'123456'});
    var DB = new MySqlDB<User>();
    DB.add(u);
    

模塊

  1. 內部模塊稱為命名空間,外部模塊稱為模塊;
  2. 模塊在自身作用域中執行,而不影響全局作用域,即模塊中的變量、函數、類等等對外部是不可見的;
  3. 外部要使用模塊內的數據,必須先讓模塊通過 export 暴露出里面的數據,外部再通過 import 引入這些數據即可;

(外部)模塊

  1. 創建目錄modules,用于存放項目的模塊,創建一個模塊db.ts
    var url = 'xxxxxx';
    
    export function getData():any[] {
        return [
            { title: '111' },
            { title: '222' },
        ]
    }
    export function save() {
        console.log('save...')
    }
    
  2. 在其他模塊中引入 db.ts 模塊中的方法
    import { getData } from './modules/db';
    
  3. 引入時起別名
    import { getData as get } from './modules/db';
    
  4. 但是,export、import 會被編譯為 exports、require(),瀏覽器默認不支持,需要使用 node 命令執行,或者借助 webpack 這樣的工具編譯為瀏覽器能執行的代碼;
  5. 暴露方式二
    export { url, getData, save }
    
  6. 暴露方式三:export default
    1. export 可以導出多次,而 export default 只能導出一次
    export default getData;
    
    1. export default 導出的是什么數據,import 導入的就是什么數據
    import getData from './modules/db';
    

命名空間

  1. 在一個模塊中,為了避免各種變量命名相沖突,可以將相似功能的函數、類、接口等放到獨立的命名空間內;
    namespace A {
        interface Animal {
            name:string;
        }
    }
    namespace B {
        interface Animal {
            name:string;
        }
    }
    
    1. 命名空間內的數據默認是私有的,外部要使用這些數據,也必須通過 export 暴露出去;
    namespace A {
        interface Animal {
            name:string;
        }
        export class Dog implements Animal {
            name:string='Joker';
            eat() {
                console.log(this.name + ' eat');
            }
        }
    }
    var d = new A.Dog();
    d.eat(); // Joker eat
    
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容