Typescript知識梳理
概述
TypeScript簡稱TS,具有類型系統,且是JavaScript的超集。 它可以編譯成普通的JavaScript代碼。TypeScript支持任意瀏覽器,任意環境,任意系統並且是開源的。通過TS能夠生成JS,TS是結構性語言,能夠類似C#和Java那樣,面向對象編程。可以采用VS或者VS Code作為編輯器。
基本類型
布爾類型:let isDone: boolean = false;
數字類型:let decLiteral: number = 6;//decLiteral: number =0xf00d;支持16進制、10進制
字符串類型:let name: string = "bob";//可以使用模板,實例如let sentence: string = `Hello, my name is ${ name }`
數組類型:let list: number[] = [1, 2, 3]; 或 let list: Array<number> = [1, 2, 3];
元組類型:let x: [string, number]=["Hello",87];
枚舉類型:enum Color {Red = 1, Green, Blue}; let c: Color = Color.Green;
任意值類型:let notSure: any = 4; notSure=‘Hello‘ ;
空類型: let unusable: void = undefined;
接口(interface)
TS中的接口不同於C#和Java有著明確的定義去規劃繼承接口的類。TS是結構性語言,更多的通過結構比對,來達到類型的兼容。
接口描述屬性:
interface LabelledValue { label: string;
size?: number } function printLabel(labelledObj: LabelledValue) {
if(labelledObj.size)
console.log(‘尺寸:‘+labelledObj.size); console.log(labelledObj.label); } let myObj= {size: 10, label: "Size 10 Object"}; printLabel(myObj);
我們在這裏並不能像在其它語言裏一樣,說傳給 printLabel 的對象實現了這個接口。我們只會去關註值的外形。 只要傳入的對象滿足上面提到的必要條件,那麽它就是被允許的。還有一點值得提的是,類型檢查器不會去檢查屬性的順序,只要相應的屬性存在並且類型也是對的就可以。size?:number標識該參數是可選類型,判斷是否傳入值可以通過if(lablelledObj.size)來確定;
接口描述函數:
接口也可以描述函數類型。為了使用接口表示函數類型,我們需要給接口定義一個調用簽名。 它就像是一個只有參數列表和返回值類型的函數定義。參數列表裏的每個參數都需要名字和類型。函數的參數名不需要與接口裏定義的名字相匹配。
interface SearchFunc { (source: string, subString: string): boolean; } let mySearch: SearchFunc; mySearch = function(source_T: string, subString_T: string)
{ let result = source.search(subString); if (result == -1) { return false; } else { return true; } }
接口描述類:
類是具有兩個類型的:靜態部分的類型和實例的類型;
//**********************報錯的代碼部分******************************
//用構造器簽名去定義一個接口並試圖定義一個類去實現這個接口時會得到一個錯誤,因為當一個類實現了一個接口時,只對其實例部分進行類型檢查。
//constructor存在於類的靜態部分,所以不在檢查的範圍內 interface ClockConstructor { new (hour: number, minute: number); } class Clock implements ClockConstructor { currentTime: Date; constructor(h: number, m: number) { } }
//*****************修改之後的代碼部分********************************
//所以針對上面錯誤,我們將接口分為2部分,靜態部分和實例部分接口。實例部分,我們采用繼承來實現;直接操作靜態部分 interface Clock
{ new (hour: number, Minute: number): ClockFun; } interface ClockFun
{ ShowData(): string; } class MyClock implements ClockFun { _hour: number; _minu: number; constructor(hour: number, Minu: number) { this._hour = hour; this._minu = Minu; } public ShowData(): string { return `當前時間:${this._hour}點${this._minu}分`; } } //靜態部分的操作 function MyCeate2() { let datm: Clock = MyClock; let Namv2 = new datm(123, 5); Namv2.ShowData(); }
接口擴展:
接口擴展包括接口之間的繼承extends和接口繼承類,接口繼承了一個類類型時,它會繼承類的成員但不包括其實現。 就好像接口聲明了所有類中存在的成員,但並沒有提供具體實現一樣。 接口同樣會繼承到類的private和protected成員。
class OtherProClass{
public height:numner;
}
interface Shape { color: string; } interface PenStroke { penWidth: number; } interface Square extends Shape, PenStroke,OtherProClass { sideLength: number; } let square = <Square>{}; square.color = "blue"; square.sideLength = 10; square.penWidth = 5.0;
square.height=100;
接口內容的混合使用:
接口即可以標識字段、方法、簽名方法、類,這些標識是可以混合定義在一個接口裏面,由於TS中接口的含義廣,所以命名是不建議用I開頭,來標識一個接口含義;
interface Counter { (start: number): string; interval: number; reset(): void; } function getCounter(): Counter
{ let counter = <Counter>function (start: number) { }; counter.interval = 123; counter.reset = function () { }; return counter; } let c = getCounter(); c(10); c.reset(); c.interval = 5.0;
聯合類型
聯合類型表示一個值可以是幾種類型之一。 我們用豎線( | )分隔每個類型,所以 number | string | boolean 表示一個值可以是 number , string ,或 boolean 。
interface Bird { fly(); layEggs(); } interface Fish { swim(); layEggs(); } function getSmallPet(): Fish | Bird { // ... } let pet = getSmallPet(); pet.layEggs(); // okay pet.swim(); // errors //為了讓這碼代碼工作,我們要使用類型斷言 let pet2 = getSmallPet(); if ((<Fish>pet).swim) { (<Fish>pet).swim(); } else{ (<Bird>pet).fly(); }
自定義的類型保護
TypeScript裏的類型保護機制讓它成為了現實。 類型保護就是一些表達式,它們會在運行時檢查以確保在某個作用域裏的類型。 要定義一個類型保護,我們只要簡單地定義一個函數,它的返回值是一個類型斷言;
//pet is Fish 就是類型斷言。一個斷言是 parameterName is Type 這種形式, parameterName 必須是來自於當前函數簽名裏的一個參數名 function isFish(pet: Fish | Bird): pet is Fish { return (<Fish>pet).swim !== undefined; } //調用斷言 if (isFish(pet) { pet.swim(); } else { pet.fly(); }
使用typeof 類型保護,來實現斷言,這些 typeof 類型保護只有2個形式能被識別: typeof v ==="typename" 和 typeof v !== "typename" , "typename" 必須是 "number" , "string" , "boolean" 或 "symbol"
function isNumber(x: any): x is number
{ return typeof x === "number"; }
instanceof 類型保護,instanceof 類型保護是通過其構造函數來細化其類型。
if (myDataNum instanceof MyClock) { console.log("輸出數據類型"); }
類型別名
類型別名會給一個類型起個新名字。 類型別名有時和接口很像,但是可以作用於原始值,聯合類型,元組以及其它任何你需要手寫的類型。另一方面,如果你無法通過接口來描述一個類型並且需要使用聯合類型或元組類型,這時通常會使用類型別名。
type XCoord = number; type YCoord = number; type XYCoord = { x: XCoord; y: YCoord }; type XYZCoord = { x: XCoord; y: YCoord; z: number }; type Coordinate = XCoord | XYCoord | XYZCoord; type CoordList = Coordinate[]; let coord: CoordList = [{ x: 10, y: 10}, { x: 0, y: 42, z: 10 }]
type Container<T> = { value: T };//泛類型
類(Class)
類是通用的代碼塊,我們在引用任何一個類成員的時候都用了 this 。 它表示我們訪問的是類的成員。構造函數不同於C#和Java,采用 constructor來標識;類可以實現封裝、繼承和多態;子類可以重寫父類的方法;在TypeScript裏,每個成員默認為 public 的。
當成員被標記成 private 時,它就不能在聲明它的類的外部訪問。TypeScript使用的是結構性類型系統。 當我們比較兩種不同的類型時,並不在乎它們從哪兒來的,如果所有成員的類型都是兼容的,我們就認為它們的類型是兼容的。然而,當我們比較帶有 private 或 protected 成員的類型的時候,情況就不同了。 如果其中一個類型裏包含一個 private 成員,那麽只有當另外一個類型中也存在這樣一個 private 成, 並且它們是來自同一處聲明時,我們才認為這兩個類型是兼容的。 對於 protected 成員也使用這個規則。protected 修飾符與 private 修飾符的行為很相似,但有一點不同, protected 成員在派生類中仍然可以訪問。
class Animal { private name: string; constructor(theName: string) { this.name = theName; } } class Rhino extends Animal { constructor() { super("Rhino"); } } class Employee { private name: string; constructor(theName: string) { this.name = theName; } } let animal = new Animal("Goat"); let rhino = new Rhino(); let employee = new Employee("Bob"); animal = rhino; animal = employee;//由於定義了Private成員,該出的成員定義不是出於同一處,所以賦值失敗
屬性參數
通過構造函數裏面,用protected、private和public修改參數,可以定義類屬性;
class Animal { constructor(private name: string) { } move(distanceInMeters: number) { console.log(`${this.name} moved ${distanceInMeters}m.`); } }
存取器
TypeScript支持getters/setters來截取對對象成員的訪問。 它能幫助你有效的控制對對象成員的訪問。
class Employee { private _fullName: string; get fullName(): string { return this._fullName; } set fullName(newName: string) { if (passcode && passcode == "secret passcode") { this._fullName = newName; } else { console.log("Error: Unauthorized update of employee!"); } } }
抽象類是供其它類繼承的基類。 他們一般不會直接被實例化。 不同於接口,抽象類可以包含成員的實現細節。 abstract 關鍵字是用於定義抽象類和在抽象類內部定義抽象方法。類分為靜態部分,用static修改,也分為實例部分;靜態部分對象,可以直接通過類來訪問;
class Greeter { static standardGreeting = "Hello, there"; greeting: string; greet() { if (this.greeting) { return "Hello, " + this.greeting; } else { return Greeter.standardGreeting; } } } let greeter1: Greeter; greeter1 = new Greeter(); console.log(greeter1.greet()); let greeterMaker: typeof Greeter = Greeter; greeterMaker.standardGreeting = "Hey there!"; let greeter2:Greeter = new greeterMaker(); console.log(greeter2.greet());
命名空間和模塊
模塊的引用
import x from "..."; 或 import x = require("...") 裏面的 ... ,等等)來定位模塊的類型信息的。“內部模塊”現在叫做“命名空間”。任何包含頂級 import 或者 export 的文件都被當成一個模塊。
導出聲明:任何聲明(比如變量,函數,類,類型別名或接口)都能夠通過添加 export 關鍵字來導出。默認導出用default
//聲明導出接口 export interface StringValidator { isAcceptable(s: string): boolean; } //聲明導出語句 class ZipCodeValidator implements StringValidator { isAcceptable(s: string) { return s.length === 5 && numberRegexp.test(s); } } export { ZipCodeValidator }; export { ZipCodeValidator as mainValidator };
導入:模塊的導入操作與導出一樣簡單。 可以使用以下 import 形式之一來導入其它模塊中的導出內容。
//可以對導入內容重命名 import { ZipCodeValidator as ZCV } from "./ZipCodeValidator"; let myValidator = new ZCV(); //將整個模塊導入到一個變量,並通過它來訪問模塊的導出部分 import * as validator from "./ZipCodeValidator"; let myValidator = new validator.ZipCodeValidator();
若要導入一個使用了 export = 的模塊時,必須使用TypeScript提供的特定語法 import let = require("module") 。
參考博客
1、TypeScript Handbook(中文版)
Typescript知識梳理