1. 程式人生 > 其它 >TypeScript快速入門

TypeScript快速入門

TypeScript 快速入門

https://blog.csdn.net/weixin_51457459/article/details/115283944

JS 自有型別系統的問題

語言型別的分類

強型別與弱型別

程式語言的劃分從安全的維度劃分

強型別:變數型別定義之後不會任意進行隱式型別轉換 (JAVA)

弱型別:會進行隱式轉換 (JS)

強型別優勢

  • 錯誤更早暴露。如:編譯時就知道程式碼的錯誤了
  • 程式碼更智慧,編碼更準確。如:編輯器知道變數型別,會實時進行提示
  • 減少不必要的型別判斷。

靜態型別與動態型別

程式語言從型別檢查的維度劃分

靜態型別:變數在宣告的時候其型別就是明確的,宣告過後他的型別是不允許進行隨意的修改的

動態型別:變數是沒有型別的,變數的型別取決於程式碼執行時值的型別

JS自有型別系統的問題

JAVASCRIPT 屬於弱型別&動態型別語言

弱型別&動態型別語言的缺點

  • “任意”
  • “不靠譜”
  • 由於JS是屬於指令碼語言,不需要進行編譯,其執行依賴執行環境。程式碼錯誤往往都是執行時才能夠發現錯誤,而不能夠在編譯的時候就知道錯誤的所在。
// 弱型別的問題
// 進行隱式轉換,使函式功能發生改變了
// 這樣的結果不是我們期待的結果,我們期待的時求兩個數的和
function add(num1, num2) {
    return num1 + num2
}

add(100, '200') // 100200

JS 為什麼會設計成弱型別&靜態型別語言哪?

這是歷史遺留問題,早期的web應用都是非常簡單的應用,不需要太多程式碼就可以實現一個應用,如果在有型別檢查會顯得複雜且多餘。但現在隨著web應用越來越複雜龐大,之前的語言設計就顯得力不從心。

JavaScript 型別系統問題解決方案

  • Flow FACEBOOK團隊推出的JS型別檢查器來進行型別檢查 Flow的文章
  • 使用 TypeScript

TypeScript

TypeScript的認識

  • 根據前面的點我們知道JavaScript語言設計的不足在於其是弱型別&動態型別的語言,不會進行型別檢查,不適合於開發大型應用
  • TypeScript是為了解決JavaScript自有型別系統的問題而誕生的,TypeScript是JavaScript的超集
  • TypeScript中也可以寫ES6+的語法,在編譯的時候會轉換為目標,相當於會有個babel的功能

快速上手

快速上手檢視官方文件:TypeScript中文官方手冊

TypeScript 的語法

TS 六種原始型別

// TypeScript 原始資料型別
// Number
const num: number = 1234
const str: string = '1234'
const _undefined: void = undefined
const _boolean: boolean = true
const _null: null = null
// const _symbol: symbol = Symbol()

編譯後:

// TypeScript 原始資料型別
// Number
var num = 1234;
var str = '1234';
var _undefined = undefined;
var _boolean = true;
var _null = null;
// const _symbol: symbol = Symbol()

需要注意的點是:

  1. 非嚴格模式下 變數可以為null || undefined || 所定義的型別,嚴格模式下 定義的變數只能夠是所定義的型別
  2. tsconfig配置檔案預設編譯程式碼是ES5,所使用的標準庫也是ES5的,ES5的ts標準庫沒有對ES6內建物件進行定義,所以使用定義symbol型別會出現錯誤。解決辦法修改tsconfig中lib配置為['ES2015', 'DOM']。DOM標準庫包含BOM DOM 所謂的標準庫就是TS對JS內建物件,api的定義,可以理解為程式碼檢查規則配置檔案。

讓TS以中文提示錯誤訊息

  • 執行npx tsc --locale zh-CN
  • vs code 中 修改設定裡面找到 typescript locale配置

TS中的Object 型別(除了原始型別以外的其他型別)

object型別指原始型別一外的其他型別

const a: object = function () { }
const b: object = / /

物件型別限制

object 物件型別的限制可以使用字面量的形式進行限制

const obj: { name: string, age: number } = { name: 'reborn', age: 18 }

陣列型別

// 表示定義的陣列必須都是數字型別
const arr: number[] = [1, 2, 3]
// 泛型定義陣列, 這個與上面是相同的意思
const arr1: Array<number> = [1, 2, 3]

元組型別的定義

元組含義是明確元素數量以及明確元素型別的陣列*

如下:定義個三個元素,第一個為number型別,第二個為string型別,第三個為boolean型別

const tuple: [number, string, boolean] = [1, '1', false,]

列舉型別

通過enum關鍵字可以定義列舉資料結構,列舉資料結構一般都是定義一些常量的,如下面定義文章的狀態型別。

const enum postStatus {
    draft = 0,
    unpublished = 1,
    published = 2
}
console.log(postStatus['draft'])

上面程式碼會被編譯成如下程式碼:

var postStatus;
(function (postStatus) {
    postStatus[postStatus["draft"] = 0] = "draft";
    postStatus[postStatus["unpublished"] = 1] = "unpublished";
    postStatus[postStatus["published"] = 2] = "published";
})(postStatus || (postStatus = {}));
console.log(postStatus['draft']);

另外一點可以直接定義常量列舉,只需要在 enum關鍵字前面加上 const,這樣編譯出來的程式碼直接是列舉值了

const enum postStatus {
    draft = 0,
    unpublished = 1,
    published = 2
}
console.log(postStatus['draft'])
console.log(0 /* 'draft' */);

注意的點:

  1. 列舉資料可以分為 字串列舉與 數字列舉
  2. 數字列舉可以不用定義值,預設會從 0 開始,如上面postStatus {draft,unpublished,published}, 會得到 {draft = 0 ……}
  3. 數字列舉定義第一個值,後面的值會預設補全。如上面postStatus {draft = 8,unpublished,published}, 會得到 {draft = 8 , unpublished = 9,published = 10 }
  4. 字串列舉必須要定義相應的值

函式型別

函式字面量宣告

fn1 函式的形參必須是兩個number 型別,該函式的返回值必須是number型別

function fn1(num1: number, num2: number): number {
    return num1 + num2
}
函式表示式宣告

函式表示式會有一個變數fn2接受一個函式,當前變數也可以進行型別定義,表面接受什麼樣的函式

如fn2表示,它只能夠接受一個為字串的引數,且沒有返回值的函式

const fn2: (greeting: string) => void = (greeting: string): void => {
    console.log(`${greeting} Reborn~`)
}

編譯完為

var fn2 = function (greeting) {
    console.log(greeting + " Reborn~");
};

Date型別

const date: Date = new Date()

正則表示式

const reg: RegExp = /^/

TS 中定義任意型別

Any型別表示,hello函式可以接受任意型別引數

any 型別不會有TS型別檢查,一般是用於相容老的程式碼

function hello(greeting: any): void { }

TS 的隱式型別推斷機制

TS 具有隱式型別推斷機制。下圖我們並沒有給變數 a 新增上型別註解,但給 a 變數賦值了number型別的2,此時 TS 就會推斷改資料型別為number型別,當我們再次給a賦值字串’3423’的時候,VsCode就會有報錯提示。

雖然說隱式型別推斷機制能夠讓我們不用去寫型別註解為我們開發提供方便,但是不建議採用次機制.

TS 型別斷言

TS 型別斷言的應用場景

// 1. 假設arr是來自 後臺介面 返回的資料
const arr = [1, 2, 3, 4]

// 2. 我們需要查詢 為 2 的資料
const target = arr.find(item => item === 2)

// 4. 這時候可以採用型別斷言,告訴 TS 我們確認改型別為 number
// - 採用 as 關鍵字
const result = target as number
// - 採用泛型也可以進行型別斷言 , 不過再寫 jsx 的時候泛型的尖括號會與html標籤進行衝突
// const result = <number> target

// 3. 此時就不能夠進行 * 運算,因為target 型別有可能為 number 有可能為 undefined
const res = result * result

TS 介面

一句話用來約束物件的結構(成員的型別、成員個數),在實際的編譯時並不會編譯為JS程式碼

// TS 介面 
// 用來約束物件成員

// 1. 通過interface關鍵字約定介面
interface Post {
    title: string
    content: string
    subTitle?: string
    // 2. 可選成員, 只讀成員, 動態成員
    // - 通過 ? 定義 可選成員
    // - 通過 readonly 關鍵字定義只讀成員
    // - 通過 物件的 key 的計算屬性定義 動態成員
    readonly summary: string
}

function printPost(post: Post): void {
    console.log(post.title)
    console.log(post.content)
    console.log(post.summary)
}

printPost({ title: 'reborn', content: 'vergood', summary: '哈哈哈哈' })


// - 通過 物件的 key 的計算屬性定義 動態成員
// 設定動態成員,就可以為物件新增任意成員,
//  下面介面表示可以為物件新增任意成員, 型別約束為了 key 為str, value str
interface dongtai {
    [dd: string]: string
}

const obj: dongtai = {
    hello: 'hello',
    age: '18'
}

編譯後的程式碼長這樣,可以看到並沒有介面

// TS 介面 
// 用來約束物件成員
function printPost(post) {
    console.log(post.title);
    console.log(post.content);
    console.log(post.summary);
}
printPost({ title: 'reborn', content: 'vergood', summary: '哈哈哈哈' });
var obj = {
    hello: 'hello',
    age: '18'
};

TS 中的類

傳統的JavaScript程式使用函式和基於原型的繼承來建立可重用的元件,但對於熟悉使用面向物件方式的程式設計師來講就有些棘手,因為他們用的是基於類的繼承並且物件是由類構建出來的。 從ECMAScript 2015,也就是ECMAScript 6開始,JavaScript程式設計師將能夠使用基於類的面向物件的方式。 使用TypeScript,我們允許開發者現在就使用這些特性,並且編譯後的JavaScript可以在所有主流瀏覽器和平臺上執行,而不需要等到下個JavaScript版本。

const { log } = console
// TS 中類的使用
// 1. 為例項新增屬性的時候,需要先對屬性進行宣告
// 3. 可以給類的成員定義訪問修飾符如 private 定製私有屬性,public定義 公有屬性(預設) protected 受保護的(只允許在子類訪問該成員)
class Person {
    // 2. ES2016 可以直接在物件中通過 變數名= 值 的形式為 例項賦值屬性
    public name: string
    // name?:string = undefined

    // 6. private 關鍵字定義的變數僅適用於當前 類的內部使用
    private age: number
    protected gender: boolean

    // 7. 通過新增 readonly 關鍵字 標註物件是隻讀屬性

    public readonly weight: number = 180

    // 4. 給建構函式定義為公有型別,可以在外部通過new 關鍵字建立例項,如果為 private 只能夠在建構函式內部建立例項
    public constructor(name: string, age: number, gender: boolean) {
        this.name = name
        this.age = age
        this.gender = gender

    }

    public sayHi(): void {
        log(this.age)
    }
}

class Student extends Person {
    public constructor(name: string, age: number) {
        super(name, age, false)
        // 5. protected 關鍵字定義的屬性僅適用於子類繼承訪問
        log(this.gender, 'protected')
    }
}

編譯後

var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var log = console.log;
// TS 中類的使用
// 1. 為例項新增屬性的時候,需要先對屬性進行宣告
// 3. 可以給類的成員定義訪問修飾符如 private 定製私有屬性,public定義 公有屬性(預設) protected 受保護的(只允許在子類訪問該成員)
var Person = /** @class */ (function () {
    // 4. 給建構函式定義為公有型別,可以在外部通過new 關鍵字建立例項,如果為 private 只能夠在建構函式內部建立例項
    function Person(name, age, gender) {
        // 7. 通過新增 readonly 關鍵字 標註物件是隻讀屬性
        this.weight = 180;
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    Person.prototype.sayHi = function () {
        log(this.age);
    };
    return Person;
}());
var Student = /** @class */ (function (_super) {
    __extends(Student, _super);
    function Student(name, age) {
        var _this = _super.call(this, name, age, false) || this;
        // 5. protected 關鍵字定義的屬性僅適用於子類繼承訪問
        log(_this.gender, 'protected');
        return _this;
    }
    return Student;
}(Person));

類中的介面

實現類的結構

需求:

現在需要定義兩個類 Person、Animal 類,都有相同行為 吃 與 行走等行為,此時我們可以通過介面進行抽象公共方法

定義類的介面,表示該資料型別要有 eatFood 和 walk 的行為(方法)

const { log } = console

interface EatAndWalk {
    eatFood(food: string): void
    walk(where: string): void
}


class Person implements EatAndWalk {
    public eatFood(food: string): void {
        log(`person eat ${food}`)
    }

    public walk(where: string): void {
        log(`person ${where} on street`)
    }
}

class Animal implements EatAndWalk {
    public eatFood(food: string): void {
        log(`animal eat ${food}`)
    }

    public walk(where: string): void {
        log(`animal ${where} on street`)
    }
}

編譯後

var log = console.log;
var Person = /** @class */ (function () {
    function Person() {
    }
    Person.prototype.eatFood = function (food) {
        log("person eat " + food);
    };
    Person.prototype.walk = function (where) {
        log("person " + where + " on street");
    };
    return Person;
}());
var Animal = /** @class */ (function () {
    function Animal() {
    }
    Animal.prototype.eatFood = function (food) {
        log("animal eat " + food);
    };
    Animal.prototype.walk = function (where) {
        log("animal " + where + " on street");
    };
    return Animal;
}());

抽象類

abstract class Animal {
    eat(food: string): void {
        console.log(`eat ${food}`)
    }
    // 也可以定義抽象方法,當父類有抽象方法的時候,其子類也必須要有所定義的方法。
    abstract run(distance: number): void
}

class Cat extends Animal {
    constructor(food: string) {
        super()
        this.eat(food)
    }

    run(distance: number): void {
        console.log(`run ${distance} meters`)
    }
}

js程式碼

var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var Animal = /** @class */ (function () {
    function Animal() {
    }
    Animal.prototype.eat = function (food) {
        console.log("eat " + food);
    };
    return Animal;
}());
var Cat = /** @class */ (function (_super) {
    __extends(Cat, _super);
    function Cat(food) {
        var _this = _super.call(this) || this;
        _this.eat(food);
        return _this;
    }
    Cat.prototype.run = function (distance) {
        console.log("run " + distance + " meters");
    };
    return Cat;
}(Animal));

泛型

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

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

function createArra<T>(length: number, value: T): T[] {
    const arr = Array<T>(length).fill(value)
    return arr
}

const resNumberArr = createArra<number>(3, 100)
console.log(resNumberArr)

const resStringArr = createArra<string>(3, '333')
console.log(resStringArr)

編譯後:

function createArra(length, value) {
    var arr = Array(length).fill(value);
    return arr;
}
var resNumberArr = createArra(3, 100);
console.log(resNumberArr);
var resStringArr = createArra(3, '333');
console.log(resStringArr);

TS 型別宣告

import { upperCase } from 'lodash'
// TS 型別宣告
// 如果引入的第三方模組不是 ts模組的的話,型別系統會失效,必須要自己手動宣告方法型別
// 1. 宣告 upperCase方法
declare function upperCase(str: string): string

// 2. 此時呼叫upperCase就會進行型別檢查
const res = upperCase('hello')