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

一、Typescript 的認識

  1. TypescriptJavascript 類型的超集,它可以編譯成純 JavascriptTypescript 可以在任何瀏覽器、任何計算機和任何操作系統上運行,並且是開源的。
  2. TS 遵循當前以及未來出現的 ECMAScript 規範。TS 不僅能兼容現有的 JavaScript 代碼,它也擁有兼容未來版本的 JavaScript 的能力。大多數 TS 的新增特性 都是基於未來的 JavaScript 提案,這意味着許多 TS代碼在將來很有可能會變成 ECMA 的標準。
  3. Typescript 的好處,如下所示:
  • TS 的靜態類型可以在開發過程中,發現潛在問題
  • 更友好的編輯器自動提示
  • 通過這些靜態類型,代碼語義更清晰易懂

二、Typescript 的類型

  1. Typescript 的靜態類型中,當一個變量是靜態類型的時候,不僅僅意味着這個變量的類型不能夠修改,還意味着這個變量的屬性和方法也基本確定了,編輯器也能夠給我們友好的提示。
  2. 在靜態類型時,當定義 ponumber 類型的數據時,它就不能夠被修改了,同時,也具有 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 無法分析變量類型的話,我們就需要使用類型註解。當 firstNumbersecondNumber 的值爲 12 的時候,TS 已經做了類型推斷,可以知道是 Number 類型,所以 total 也是 Number 類型。any 類型,允許在編譯時可選擇地包含或移除類型檢查。如果不加類型註解,firstlast 就會顯示 any, 不知道變量是什麼類型,加類型註解,firstlast 就會顯示 number,最後 getTotal2 傳入值後,tot 也是 Number。對於 obj 對象,TS 也做了類型推斷,nameString 類型,ageNumber 類型,代碼如下所示:

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 類型相反,它表示沒有任何類型,只能爲它賦予 undefinednull。類型爲 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. TypeScriptJavaScript 一樣可以操作數組元素,它有兩種方式可以定義數組。第一種,可以在元素類型後面接上 [],表示由此類型元素組成的一個數組。第二種,可以使用數組泛型,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版本。定義一個類,有屬性、方法和構造函數。基於類的程序設計中一種最基本的模式是允許使用繼承來擴展現有的類。子類可以繼承父類,也可以重寫父類。如下,Student3Person3 的子類,有 Person3name 屬性和 getName 方法,也可以新增屬性和方法,同時,也可以重寫父類的 getName 方法。super 是指父類,通過 super.getName() 調用父類 Person3getName() 方法。對於 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, publicpublic 允許在類的內外被調用,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,給屬性添加私有保護,只能夠在類中訪問,在類外是訪問不到的。getset 是提供了一種類外訪問私有屬性的方式,可以在外部去獲取和設置私有屬性,但是不改變在類中的值。如下,_name 是私有屬性,通過 getsetname 是 類 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 再繼續封裝,添加新的對應的屬性,StudentTeacher 添加對應的 studentAgeteacherAge 屬性,這樣就實現了接口的封裝,得以更好的利用,接口的適用,代碼如下所示:
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);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章