核心概念
接口
參數校驗
interface LabelledValue {
label: string;
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
接口可以用來校驗參數的類型,參數是否存在等等。
屬性預定義
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
let newSquare = {color: "white", area: 100};
if (config.clor) {
// Error: Property 'clor' does not exist on type 'SquareConfig'
newSquare.color = config.clor;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({color: "black"});
屬性只讀
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!
注意事項
對象字面量形式會被特殊對待而且會經過額外屬性檢查
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
let newSquare = { color: "white", area: 100 };
if (config.clor) {
// Error: Property 'clor' does not exist on type 'SquareConfig'
newSquare.color = config.clor;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({ color: "black" });
createSquare({ colour: "red", width: 100 });
// Error: Property 'colour does no exist on type 'SquareConfig'
如果想解決這個問題有三種解決辦法
// 1. 將對象字面量複製給變量,傳入變量不會被進行額外屬性檢查
let skipLiteral = { colour: "red", width: 100 };
createSquare(skipLiteral);
// 2.採用類型斷言,使用as操作符,告訴編譯器我傳入的參數對象就是你要的類型
createSquare({ colour: "red", width: 100 } as SquareConfig);
// 3.第三種方式,添加萬能校驗符,這個符號會包容color、width之外所有其他屬性,慎用
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
函數類型
interface SearchFunc {
(source: string, subString: string): boolean;
}
可索引的類型
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
ts支持兩種索引簽名,字符串和數值,同時使用的時候,數值索引返回的類型必須是字符串索引返回類型的子類,因爲js解析的時候會將數值索引轉換爲字符串索引,所以數值索引返回的類型必須與字符串返回的類型保持一致(或子類)。
class Animal {
name: string;
}
class Dog extends Animal {
breed: string;
}
// 錯誤:使用數值型的字符串索引,有時會得到完全不同的Animal!
interface NotOkay {
[x: number]: Animal;
[x: string]: Dog;
}
類類型
與C#或Java裏接口的基本作用一樣,TypeScript也能夠用它來明確的強制一個類去符合某種契約。
interface ClockInterface {
currentTime: Date;
}
class Clock implements ClockInterface {
currentTime: Date;
constructor(h: number, m: number) { }
}
接口繼承
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
類
繼承 extends
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();
存取器
let passcode = "secret passcode";
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!");
}
}
}
let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
alert(employee.fullName);
}
靜態屬性 static
class Grid {
static origin = {x: 0, y: 0};
calculateDistanceFromOrigin(point: {x: number; y: number;}) {
let xDist = (point.x - Grid.origin.x);
let yDist = (point.y - Grid.origin.y);
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor (public scale: number) { }
}
let grid1 = new Grid(1.0); // 1x scale
let grid2 = new Grid(5.0); // 5x scale
console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));
把類當做接口使用
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
函數
let myAdd: (x: number, y: number) => number =
function(x: number, y: number): number { return x + y; };
重載
方法是爲同一個函數提供多個函數類型定義來進行函數重載。 編譯器會根據這個列表去處理函數的調用。
let suits = ["hearts", "spades", "clubs", "diamonds"];
function pickCard(x: {suit: string; card: number; }[]): number;
function pickCard(x: number): {suit: string; card: number; };
function pickCard(x): any {
// Check to see if we're working with an object/array
// if so, they gave us the deck and we'll pick the card
if (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
// Otherwise just let them pick the card
else if (typeof x == "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
}
let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);
let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);
泛型
泛型接口
function identity<T>(arg: T): T {
return arg;
}
使用尖括號傳入泛型變量,它會根據參會類型智能賦值。比如arg是number,那麼T就會被賦值爲number。
泛型類
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
在這裏,泛型作爲一種變量,可供用戶調用的時候動態約束類屬性。
在泛型約束中使用類型參數
function getProperty(obj: T, key: K) {
return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a"); // okay
getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.