一、Typescript 的認識
-
是Typescript
類型的超集,它可以編譯成純Javascript
。Javascript
可以在任何浏覽器、任何計算機和任何作業系統上運作,并且是開源的。Typescript
-
遵循目前以及未來出現的TS
規範。ECMAScript
不僅能相容現有的TS
代碼,它也擁有相容未來版本的JavaScript
的能力。大多數JavaScript
的新增特性 都是基于未來的TS
提案,這意味着許多JavaScript
代碼在将來很有可能會變成TS
的标準。ECMA
-
的好處,如下所示:Typescript
-
的靜态類型可以在開發過程中,發現潛在問題TS
- 更友好的編輯器自動提示
- 通過這些靜态類型,代碼語義更清晰易懂
二、Typescript 的類型
- 在
的靜态類型中,當一個變量是靜态類型的時候,不僅僅意味着這個變量的類型不能夠修改,還意味着這個變量的屬性和方法也基本确定了,編輯器也能夠給我們友好的提示。Typescript
- 在靜态類型時,當定義
為po
類型的資料時,它就不能夠被修改了,同時,也具有number
的方法。當定義number
為自定義類型時,也意味着不能夠被修改,具有Point
的屬性和方法,代碼如下所示:Point
const po: number = 2020;
po.toString();
interface Point {
x: number;
y: number;
}
const point: Point = {
x: 3,
y: 4,
};
-
分為基礎類型和對象類型。在基礎類型中,包括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;
};
-
類型注解, 我們來告訴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,
};
- 在函數相關類型中,在給函數的參數做了類型注解以後,對于函數的傳回值也要做一層限制,加上類型注解。
類型像是與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 的數組、元組和接口
-
像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 }];
- 元組
,元組類型允許表示一個已知元素數量和類型的數組,各元素的類型不必相同。當數組的長度和類型固定的時候,就可以使用元組進行限制該數組。元組中每一個元素的數量和類型都是與其一一對應的,代碼如下所示:tuple
const studentInfo: [string, string, number] = ["Jack", "Tom", 123];
const studentList: [string, string, number][] = [
["Tom1", "Jack1", 21],
["Tom2", "Jack2", 22],
["Tom3", "Jack3", 23],
];
-
的核心原則之一是對值所具有的結構進行類型檢查。 它有時被稱做“鴨式辨型法”或“結構性子類型化”。 在TypeScript
裡,接口的作用就是為這些類型命名和為你的代碼或第三方代碼定義契約。TypeScript
接口,和Interface
相類似,但并不完全一緻。類型檢查器不會去檢查屬性的順序,隻要相應的屬性存在并且類型也是對的就可以,有幾種情況,如下所示:type
- 可選屬性,接口裡的屬性不全都是必需的,與普通的接口定義差不多,隻是在可選屬性名字定義的後面加一個
符号,好處是可以對可能存在的屬性進行預定義和捕獲引用了不存在的屬性時的錯誤。?
- 隻讀屬性,一些對象屬性隻能在對象剛剛建立的時候修改其值。 你可以在屬性名前用
來指定隻讀屬性。最簡單判斷該用readonly
還是readonly
的方法是看要把它做為變量使用還是做為一個屬性。做為變量使用的話用const
,若做為屬性則使用const
。readonly
- 額外的屬性檢查,當一個對象字面量裡聲明了接口中不存在的屬性時,會報不存在錯誤,即使該屬性已設定為可選屬性,因為該對象字面量會被特殊對待而且會經過額外屬性檢查。繞開額外屬性檢查的方法有,使用類型斷言,添加一個字元串索引簽名,但是前提是你能夠确定這個對象可能具有某些做為特殊用途使用的額外屬性,将該對象指派給另一個變量。
- 函數類型,接口也可以描述函數類型,需要給接口定義一個調用簽名。 它就像是一個隻有參數清單和傳回值類型的函數定義。參數清單裡的每個參數都需要名字和類型。
- 繼承接口,能夠從一個接口裡複制成員到另一個接口裡,可以更靈活地将接口分割到可重用的子產品裡。一個接口可以繼承多個接口,建立出多個接口的合成接口。
- 實作接口,類類型,在接口中描述一個方法,通過
在類裡實作它,使用接口的部分内容。接口描述了類的公共部分,而不是公共和私有兩部分。 它不會幫你檢查類是否具有某些私有成員。implements
- 對于
接口,代碼如下所示: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 的類
- 類,使用這些特性,于類的面向對象的方式,并且編譯後的
可以在所有主流浏覽器和平台上運作,而不需要等到下個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());
- 對于類中的通路類型,分為三種,
。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);
-
支援通過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);
- 抽象類做為其它派生類的基類使用,它們一般不會直接被執行個體化。 不同于接口,抽象類可以包含成員的實作細節,
關鍵字是用于定義抽象類和在抽象類内部定義抽象方法。抽象類中的抽象方法不包含具體實作并且必須在派生類中實作。 抽象方法的文法與接口方法相似, 兩者都是定義方法簽名但不包含方法體。然而,抽象方法必須包含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);