天天看點

Typescript 的基礎文法之認識、類數組、元組、接口和類

一、Typescript 的認識

  1. Typescript

    Javascript

    類型的超集,它可以編譯成純

    Javascript

    Typescript

    可以在任何浏覽器、任何計算機和任何作業系統上運作,并且是開源的。
  2. TS

    遵循目前以及未來出現的

    ECMAScript

    規範。

    TS

    不僅能相容現有的

    JavaScript

    代碼,它也擁有相容未來版本的

    JavaScript

    的能力。大多數

    TS

    的新增特性 都是基于未來的

    JavaScript

    提案,這意味着許多

    TS

    代碼在将來很有可能會變成

    ECMA

    的标準。
  3. Typescript

    的好處,如下所示:
  • TS

    的靜态類型可以在開發過程中,發現潛在問題
  • 更友好的編輯器自動提示
  • 通過這些靜态類型,代碼語義更清晰易懂

二、Typescript 的類型

  1. Typescript

    的靜态類型中,當一個變量是靜态類型的時候,不僅僅意味着這個變量的類型不能夠修改,還意味着這個變量的屬性和方法也基本确定了,編輯器也能夠給我們友好的提示。
  2. 在靜态類型時,當定義

    po

    number

    類型的資料時,它就不能夠被修改了,同時,也具有

    number

    的方法。當定義

    Point

    為自定義類型時,也意味着不能夠被修改,具有

    Point

    的屬性和方法,代碼如下所示:
const po: number = 2020;

po.toString();

interface Point {
  x: number;
  y: number;
}

const point: Point = {
  x: 3,
  y: 4,
};

           
  1. Typescript

    分為基礎類型和對象類型。在基礎類型中,包括

    number, string, null, undefined, symbol, boolean、void

    等等。在對象類型中,就是非基礎類型,包括 對象類型,數組類型,類類型,函數類型等等,代碼如下所示:
const count: number = 123;
const firstName: string = "zhangsan";

class Person {}

const student: { name: string; age: number } = { name: "zhangsan", age: 20 };

const numbers: number[] = [1, 2, 3];

const Jack: Person = new Person();

const getTotal: () => number = () => {
  return 123;
};

           
  1. type annotation

    類型注解, 我們來告訴

    TS

    變量是什麼類型。

    type inference

    類型推斷,

    TS

    會自動的去嘗試分析變量的類型。如果

    TS

    能夠自動分析變量類型,我們就什麼也不需要做了。如果

    TS

    無法分析變量類型的話,我們就需要使用類型注解。當

    firstNumber

    secondNumber

    的值為

    1

    2

    的時候,

    TS

    已經做了類型推斷,可以知道是

    Number

    類型,是以

    total

    也是

    Number

    類型。

    any

    類型,允許在編譯時可選擇地包含或移除類型檢查。如果不加類型注解,

    first

    last

    就會顯示

    any

    , 不知道變量是什麼類型,加類型注解,

    first

    last

    就會顯示

    number

    ,最後

    getTotal2

    傳入值後,

    tot

    也是

    Number

    。對于

    obj

    對象,

    TS

    也做了類型推斷,

    name

    String

    類型,

    age

    Number

    類型,代碼如下所示:
let countInference = 123;

const firstNumber = 1;
const secondNumber = 2;
const total = firstNumber + secondNumber;

function getTotal2(first: number, last: number) {
  return first + last;
}

const tot = getTotal2(1, 2);

const obj = {
  name: "Jack",
  age: 18,
};

           
  1. 在函數相關類型中,在給函數的參數做了類型注解以後,對于函數的傳回值也要做一層限制,加上類型注解。

    void

    類型像是與

    any

    類型相反,它表示沒有任何類型,隻能為它賦予

    undefined

    null

    。類型為

    void

    時,定義

    sayHello()

    函數為空,沒有傳回值,不能

    return

    ,如果有傳回值就會報錯。

    never

    類型表示的是那些永不存在的值的類型,是任何類型的子類型,也可以指派給任何類型。類型為

    never

    時,定義

    errorEmitter()

    函數永遠不可能執行到最後一步。在多個參數的函數中進行解構指派,需要以對象的形式,并且進行參數的類型注解以及函數傳回值的類型注解。對于單個參數的函數中進行解構指派,同樣進行類型注解,代碼如下所示:
function add(first: number, last: number): number {
  return first + last;
}

const total2 = add(1, 2);

function sayHello(): void {
  console.log("hello");
}

function errorEmitter(): never {
   throw new Error()
   console.log(123)
}

function add2({ first, second }: { first: number; second: number }): number {
  return first + second;
}

function getNumber({ first }: { first: number }) {
  return first;
}

const total3 = add2({ first: 1, second: 2 });
const count3 = getNumber({ first: 1 });

           

三、Typescript 的數組、元組和接口

  1. TypeScript

    JavaScript

    一樣可以操作數組元素,它有兩種方式可以定義數組。第一種,可以在元素類型後面接上

    []

    ,表示由此類型元素組成的一個數組。第二種,可以使用數組泛型,

    Array<元素類型>

    。對于數組,基礎類型,後面需要跟上

    []

    以及它前面的數組的類型注解。對于數組,對象類型,可以使用

    type alias

    類型别名,後面需要跟上

    []

    以及它前面的别名的類型注解,代碼如下所示:
const arr: (number | string)[] = [1, 2, 3];
const stringArr: string[] = ["1", "2", "3"];
const undefindArr: undefined[] = [undefined];

type User = { name: string; age: number };

const objectArr: User[] = [{ name: "zhangsan", age: 21 }];

class Teacher {
  name: string;
  age: number;
}

const objectArr2: Teacher[] = [new Teacher(), { name: "zhangsan", age: 21 }];
           
  1. 元組

    tuple

    ,元組類型允許表示一個已知元素數量和類型的數組,各元素的類型不必相同。當數組的長度和類型固定的時候,就可以使用元組進行限制該數組。元組中每一個元素的數量和類型都是與其一一對應的,代碼如下所示:
const studentInfo: [string, string, number] = ["Jack", "Tom", 123];

const studentList: [string, string, number][] = [
  ["Tom1", "Jack1", 21],
  ["Tom2", "Jack2", 22],
  ["Tom3", "Jack3", 23],
];

           
  1. TypeScript

    的核心原則之一是對值所具有的結構進行類型檢查。 它有時被稱做“鴨式辨型法”或“結構性子類型化”。 在

    TypeScript

    裡,接口的作用就是為這些類型命名和為你的代碼或第三方代碼定義契約。

    Interface

    接口,和

    type

    相類似,但并不完全一緻。類型檢查器不會去檢查屬性的順序,隻要相應的屬性存在并且類型也是對的就可以,有幾種情況,如下所示:
  • 可選屬性,接口裡的屬性不全都是必需的,與普通的接口定義差不多,隻是在可選屬性名字定義的後面加一個

    ?

    符号,好處是可以對可能存在的屬性進行預定義和捕獲引用了不存在的屬性時的錯誤。
  • 隻讀屬性,一些對象屬性隻能在對象剛剛建立的時候修改其值。 你可以在屬性名前用

    readonly

    來指定隻讀屬性。最簡單判斷該用

    readonly

    還是

    const

    的方法是看要把它做為變量使用還是做為一個屬性。做為變量使用的話用

    const

    ,若做為屬性則使用

    readonly

  • 額外的屬性檢查,當一個對象字面量裡聲明了接口中不存在的屬性時,會報不存在錯誤,即使該屬性已設定為可選屬性,因為該對象字面量會被特殊對待而且會經過額外屬性檢查。繞開額外屬性檢查的方法有,使用類型斷言,添加一個字元串索引簽名,但是前提是你能夠确定這個對象可能具有某些做為特殊用途使用的額外屬性,将該對象指派給另一個變量。
  • 函數類型,接口也可以描述函數類型,需要給接口定義一個調用簽名。 它就像是一個隻有參數清單和傳回值類型的函數定義。參數清單裡的每個參數都需要名字和類型。
  • 繼承接口,能夠從一個接口裡複制成員到另一個接口裡,可以更靈活地将接口分割到可重用的子產品裡。一個接口可以繼承多個接口,建立出多個接口的合成接口。
  • 實作接口,類類型,在接口中描述一個方法,通過

    implements

    在類裡實作它,使用接口的部分内容。接口描述了類的公共部分,而不是公共和私有兩部分。 它不會幫你檢查類是否具有某些私有成員。
  1. 對于

    interface

    接口,代碼如下所示:
interface Person {
  name2: string;
  age?: number;
  [propName: string]: any;
  say(): string;
}

interface Teacher2 extends Person {
  teach(): string;
}

interface SayHi {
  (word: string): string;
}

const getPersonName = (person: Person): void => {
  console.log(person.name2);
};

const setPersonName = (person: Teacher2, name: string): void => {
  person.name2 = name;
};

const personName = {
  name2: "Jack",
  sex: "man",
  say() {
    return "say hello";
  },
};

const say: SayHi = (word: string) => {
  return word;
};

           

四、Typescript 的類

  1. 類,使用這些特性,于類的面向對象的方式,并且編譯後的

    JavaScript

    可以在所有主流浏覽器和平台上運作,而不需要等到下個

    JavaScript

    版本。定義一個類,有屬性、方法和構造函數。基于類的程式設計中一種最基本的模式是允許使用繼承來擴充現有的類。子類可以繼承父類,也可以重寫父類。如下,

    Student3

    Person3

    的子類,有

    Person3

    name

    屬性和

    getName

    方法,也可以新增屬性和方法,同時,也可以重寫父類的

    getName

    方法。

    super

    是指父類,通過

    super.getName()

    調用父類

    Person3

    getName()

    方法。對于

    super

    使用場景,當一個類覆寫掉父類的方法,重寫了父類的方法,但是它還想要掉父類的方法,就可以使用

    super

    ,代碼如下所示:
class Person3 {
  name: "LiSi";
  getName() {
    return this.name;
  }
}

class Student3 extends Person3 {
  getLearnName() {
    return "Typescript";
  }
  getName() {
    return super.getName() + "WangWu";
  }
}

const person3 = new Person3();
console.log(person3.getName());

const student3 = new Student3();
console.log(student3.getLearnName());

           
  1. 對于類中的通路類型,分為三種,

    private, protected, public

    public

    允許在類的内外被調用,

    private

    允許在類内被使用,

    protected

    允許在類内及繼承的子類中使用。當一個類中,屬性和方法什麼都不寫,說明是

    public

    類型,預設是隐藏的。對于

    constructor

    構造器,當執行個體化一個類的時候,構造器會在建立的時候自動執行,而傳遞類的屬性的時候,實際上會被這個類的構造器所接收到。當子類繼承于父類的時候,如果想要私有構造器

    constructor

    ,必須使用

    super

    調用父類的構造器。如果父類構造器為空,沒有使用

    constructor

    ,那麼也需要使用

    super

    ,傳入空值,代碼如下所示:
class Person4 {
  protected name: string;
  public sayHi() {
    console.log("hi");
  }
  private sayHe() {
    console.log("he");
  }
}

class Student4 extends Person4 {
  public satBye() {
    this.sayHi();
  }
}

const person = new Person4();
// person.name = "ZhaoLiu";
// console.log(person.name);
person.sayHi();

class Person5 {
  constructor(public name: string) {}
}

class Teacher5 extends Person5 {
  constructor(public age: number) {
    super("Lucy");
  }
}

const person5 = new Person5("XiongDa");
console.log(person5.name);

const teacher5 = new Teacher5(29);
console.log(teacher5.name);
console.log(teacher5.age);

           
  1. TypeScript

    支援通過

    getters/setters

    來截取對對象成員的通路, 它能幫助你有效的控制對對象成員的通路。存取器要求你将編譯器設定為輸出

    ECMAScript 5

    或更高, 不支援降級到

    ECMAScript 3

    。 其次,隻帶有

    get

    不帶有

    set

    的存取器自動被推斷為

    readonly

    。建立類的靜态成員,這些屬性存在于類本身上面而不是類的執行個體上,可以使用

    static

    。在類中的構造器中,添加

    private

    ,給屬性添加私有保護,隻能夠在類中通路,在類外是通路不到的。

    get

    set

    是提供了一種類外通路私有屬性的方式,可以在外部去擷取和設定私有屬性,但是不改變在類中的值。如下,

    _name

    是私有屬性,通過

    get

    set

    name

    是 類

    Person6

    執行個體化對象

    person6

    的一個屬性,

    get

    是可讀,

    set

    是可寫,代碼如下所示:
class Person6 {
  constructor(private _name: string) {}
  get name() {
    return this._name + "Tom";
  }
  set name(name: string) {
    const realName = name.split(" ")[0];
    this._name = realName;
  }
}

const person6 = new Person6("Jake");
console.log(person6.name);

person6.name = "zhang san";
console.log(person6.name);

// 單例模式

class Demo {
  private static instance: Demo;
  private constructor(public name: string) {}

  static getInstance() {
    if (!this.instance) {
      this.instance = new Demo("dell lee");
    }
    return this.instance;
  }
}

const demo1 = Demo.getInstance();
const demo2 = Demo.getInstance();
console.log(demo1.name);
console.log(demo2.name);

           
  1. 抽象類做為其它派生類的基類使用,它們一般不會直接被執行個體化。 不同于接口,抽象類可以包含成員的實作細節,

    abstract

    關鍵字是用于定義抽象類和在抽象類内部定義抽象方法。抽象類中的抽象方法不包含具體實作并且必須在派生類中實作。 抽象方法的文法與接口方法相似, 兩者都是定義方法簽名但不包含方法體。然而,抽象方法必須包含

    abstract

    關鍵字并且可以包含通路修飾符。當如果想要類中的屬性隻能夠隻讀,不能夠寫,第一種方式是設定類中的屬性為

    public

    公有,設定權限為

    readonly

    隻讀。第二種方式是 設定類中的屬性為

    private

    私有,通過

    get

    讓屬性具有隻讀的權限。抽象類,通過

    abstract

    定義一個抽象類

    Geom

    ,通過

    abstract

    定義一個抽象方法

    getArea()

    ,不包含具體實作但是必須在派生類中實作,

    Circle

    是繼承于 抽象類

    Geom

    ,是它的基類,必須有抽象類中抽象方法的具體實作

    getArea()

    。對于具有相同屬性的接口進行一次封裝,

    Person

    都具有

    name

    屬性,對于

    Person

    接口也有不同的實作,可以繼承

    Person

    再繼續封裝,添加新的對應的屬性,

    Student

    Teacher

    添加對應的

    studentAge

    teacherAge

    屬性,這樣就實作了接口的封裝,得以更好的利用,接口的适用,代碼如下所示:
class Person7 {
  public readonly name: string;
  constructor(name: string) {
    this.name = name;
  }
}

const person7 = new Person7("Tom");
console.log(person7.name);

abstract class Geom {
  width: number;
  getType() {
    return "Gemo";
  }
  abstract getArea(): number;
}

class Circle extends Geom {
  getArea() {
    return 123;
  }
}

interface Person {
  name: string;
}

interface Student extends Person {
  studentAge: number;
}

interface Teacher extends Person {
  teacherAge: number;
}

const student = {
  name: "ZhangSan",
  studentAge: 18,
};

const teacher = {
  name: "LiSi",
  teacherAge: 28,
};

const getUserInfo = (user: Person) => {
  console.log(user.name);
};

getUserInfo(student);
getUserInfo(teacher);

           

繼續閱讀