在面向?qū)ο笳Z言中,接口(Interfaces)是一個很重要的概念,它是對行為的抽象,而具體如何行動需要由類(classes)去實現(xiàn)(implements),TypeScript 中的接口除了可用于對類的一部分行為進行抽象以外,也常用于對對象的形狀(Shape)進行描述。
接口初探
類型檢查器不會去檢查屬性的順序,只要相應(yīng)的屬性存在并且類型也是對的就可以。
interface IPerson{
name: string;
age: number
}
function say(person: IPerson): void {
console.log(`my name is ${person.name}, and age is ${person.age}`)
}
let me = {
name: 'funlee',
age: 18
}
say(me); // my name is funlee, and age is 18
可選屬性
定義可選屬性的接口,只需在可選屬性名字的定義后面加一個 ? 符號
interface IPerson {
name: string;
age: number;
love?: string
}
function say(person: IPerson): void {
if(person.hasOwnProperty('love')) {
console.log(`my name is ${person.name}, my age is ${person.age}, and I love ${person.love}`);
} else {
console.log(`my name is ${person.name}, and age is ${person.age}`);
}
}
let me = {
name: 'funlee',
age: 18,
love: 'TS'
}
let you = {
name: 'Jack',
age: 18
}
say(me); // my name is funlee, my age is 18, and I love TS
say(you) // my name is Jack, and age is 18
只讀屬性
定義只讀屬性的接口,只需在只讀屬性名前加 readonly
interface IPerson {
readonly name: string;
age: number;
love?: string
}
let me: IPerson = {
name: 'funlee',
age: 18
}
me.name = 'new name'; // error!
額外的屬性檢查
當(dāng)一個對象字面量里聲明了接口中不存在的屬性時,會報不存在錯誤,即使該屬性已設(shè)置為可選屬性,因為該對象字面量會被特殊對待而且會經(jīng)過額外屬性檢查,繞開額外屬性檢查的方法如下:
- 使用類型斷言
- 添加一個字符串索引簽名,前提是你能夠確定這個對象可能具有某些做為特殊用途使用的額外屬性
- 將該對象賦值給另一個變量
// 錯誤寫法,會進行額外檢查
interface IPerson {
name: string;
age?: number;
}
let me: IPerson = {
name: 'funlee',
love: 'TS'
}
// 方法一:類型斷言
interface IPerson {
name: string;
age?: number;
}
let me = {
name: 'funlee',
love: 'TS'
} as IPerson
// 方法二:字符串索引簽名
interface IPerson {
name: string;
age?: number;
[propName: string]: any;
}
let me: IPerson = {
name: 'funlee',
love: 'TS'
}
// 方法三:賦值給另一個變量
interface IPerson {
name: string;
age?: number;
}
let me = {
name: 'funlee',
love: 'TS'
}
let you: IPerson = me;
函數(shù)類型
接口可以描述函數(shù)類型,它定義了函數(shù)的參數(shù)列表和返回值類型,參數(shù)列表里的每個參數(shù)都需要名字和類型,函數(shù)的參數(shù)名不需要與接口里定義的名字相匹配,只需要類型兼容就可以了。
let getArea: (width: number, height: number) => number = (w: number, h: number): number =>{
return w * h;
}
console.log(getArea(5, 6)) // 30
可索引的類型
接口可以描述那些能夠通過索引得到的類型,可索引類型具有一個索引簽名,它描述了對象索引的類型,還有相應(yīng)的索引值類型,索引簽名支持兩種類型:number 和 string,但是由于 number 實際上會被轉(zhuǎn)化為 string 類型(根據(jù)對象 key 的性質(zhì)),所以需要遵守:number 索引的返回值類型是 string 索引的返回值類型的子類型。
interface IPerson {
[index: string]: string;
}
let me: IPerson = {love: 'TS'}
me.name = 'funlee';
me.age = 18; // error
如果 interface 里還聲明了一個和索引簽名索引返回值類型不匹配的屬性,會報錯
interface ITest {
[index: string]: string;
name: string;
age: 18; // 報錯,因為返回值類型是number,不符合string類型
}
還可以聲明一個 readonly 的索引簽名
interface IPerson {
readonly [index: string]: string;
}
let p: IPerson = {name: 'funlee'};
p.love = 'TS'; // error
類類型
typescript 里也允許像 Java、C# 那樣,讓一個 class 去實現(xiàn)一個 interface;但是需要注意的是,接口描述的是類的公共部分,而不是公共和私有兩部分,所以不會檢查類是否具有某些私有成員。
interface ISome {
prop: string // 描述一個屬性
method(paramA: string, paramB: number) // 描述一個方法
}
class A implements ISome {
prop: 'propValue'
method(a: string, b: number) {
// ...
}
constructor(paramA: number){
// ...
}
}
靜態(tài)部分與實例部分
首先看一個示例:用構(gòu)造器簽名定義一個接口,并試圖實現(xiàn)這個接口:
interface Person {
new(name: string)
}
class People implements Person {
constructor(name: string) {
// ...
}
}
// 報錯:no match for the signature 'new (name: string): any'.
這是因為:當(dāng)類實現(xiàn)一個接口時,只對實例部分進行類型檢查,而constructor存在于靜態(tài)部分,所以不在檢查的范圍內(nèi)。
所以做法如下:
// 針對類構(gòu)造函數(shù)的接口
interface CPerson {
new(name: string)
}
// 針對類的接口
interface IPerson {
name: string
age: number
}
function create(c: CPerson, name: string): IPerson {
return new c(name)
}
class People implements IPerson {
name: string
age: number
}
let p = create(People, 'funlee') // 可以
繼承接口
和類一樣,接口也可以相互繼承,如:
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
const square = <Square>{};
square.color = 'blue';
square.sideLength = 10;
同時,一個接口也可以繼承多個接口,創(chuàng)建出多個接口的合成接口,如:
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength
}
const square = <Square>{};
square.color = 'blue';
square.sideLength = 10;
square.penWidth = 5.0;
混合類型
允許讓一個對象同時作為函數(shù)和對象使用,并帶有額外的屬性,如:
interface MixedDemo {
(str: string): void;
defaultStr: string;
}
function foo(): MixedDemo {
let x = <MixedDemo>function(str: string){
console.log(str)
}
x.defaultStr = 'Hello, world'
return x
}
let c = foo();
c('This is a function') // 'This is a function'
console.log(c.defaultStr) // 'Hello, world'
接口繼承類
接口可以繼承自一個類,從而像聲明了所有類中存在的成員,并且private和protected成員也會被繼承,這意味著:只有類自己或子類能夠?qū)崿F(xiàn)該接口,例子如:
class A {
protected propA: string
}
interface I extends A {
method(): void
}
// 下面這種做法會報錯
class C implements A {
// 因為propA是類A的保護成員,只有自身和子類可實現(xiàn)
// 但類C不是A的子類
protected propA: string
method() {}
}
// 下面這種做法則是允許的
class C extends A implements A {
protected propA: string
method() {}
}