TS中的接口

本文目錄:

  • 1.接口的概念
  • 2.可選屬性和只讀屬性
  • 3.任意屬性
  • 4.函數類型
  • 5.可索引屬性
  • 6.類接口
  • 8.接口繼承接口
  • 9.接口繼承類
  • 10.類實現(implements)接口

1.接口的概念

  • 是對行爲的抽象,而具體如何行動需要由類(classes)去實現(implement)
  • 在TypeScript中,我們使用接口(Interfaces)來定義對象的類型。除了可用於對類的一部分行爲進行抽象以外,也常用於對「對象的形狀(Shape)」進行描述
    • TypeScript`的核心原則之一是對值所具有的結構進行類型檢查, 在TypeScript裏,接口的作用就是爲這些類型命名和爲你的代碼或第三方代碼定義契約
interface Animal {
   color: string;
   height: number;
}

賦值的時候,變量的形狀必須和接口的形狀保持一致

const labelVal: Animal = {
   color:'灰色';
   height: 56
};

2.可選屬性和只讀屬性

有時我們希望不要完全匹配一個形狀,那麼可以用可選屬性

interface Person {
  name: string;
  age: number;
  car?: string;
}
let Lucy: Person = {
  name: 'Lucy Lucy',
  age: 18,
  // car: '寶馬'
  // house: '別野'
};

可選屬性的含義是該屬性可以不存在 例如我們的car屬性.但仍然不允許添加未定義的屬性 例如上面的house屬性

一些對象屬性只能在對象剛剛創建的時候修改其值。 你可以在屬性名前用 readonly來指定只讀屬性
使用場景:const作爲變量使用和readonly作爲屬性使用

interface Point {
  readonly x: number;
  readonly y: number;
}
let p1: Point = { x: 10, y: 20 };

p1.x = 5; 這樣就會報錯, 說不能分配一個X值,因爲它是隻讀屬性
關於這裏的 let和const的寫法 最簡單判斷該用readonly還是const的方法是看要把它做爲變量使用還是做爲一個屬性。 做爲變量使用的話用 const,若做爲屬性則使用readonly

3.任意屬性

一個接口可能需要它除了具有我們需要的屬性以爲,還可以包含任意的其他屬性,這時就要用到任意屬性
只要使用了任意屬性,就要保證確定屬性和可選屬性的類型都必須是它的類型的子集

interface Person {
  name: string;
  age?: number;
  // 這種方式也叫 字符串索引簽名
  // [propName: string]: any;
  [propName: string]: number | string;
}
let tom: Person = {
  name: 'Tommy',
  // age: 20,
  addr: '北京'
};
console.log(tom);

上例中 任意屬性的值允許的是string, 但可選屬性age的值確實number, number不是string類型的子屬性,所以編譯報錯。應該將上例的任意屬性 變換爲 [propName: string]: any; 或者是 number|string,這樣的話addr也都不會報錯。

4.函數類型

  • 接口能夠描述JavaScript中對象擁有的各種各樣的外形。 除了描述帶有屬性的普通對象外,接口也可以描述函數類型。
  • 函數的參數會逐個進行檢查,要求對應位置上的參數類型是兼容的(通俗的講就是函數調用和函數接口定義的參數位置和都必須一一對應)

之前我們在學習函數的時候給大家提過一點,函數也是一種數據類型,我們也可以通過接口的形式定義一個函數類型
函數表達式的方式定義add,add在此時是通過類型推斷的方式被動的被定義爲函數類型

let add = function(x: number, y: number): number {
  return x + y;
};
add(1, 2);

接下來通過手動的方式將add1定義爲函數類型:

let add1: (x: number, y: number) => number = function(x: number, y: number): number {
  return x + y;
};
add1(1, 2);

上面代碼的最大缺點是不方便進行復用
現在使用函數類型接口進行更優雅和更易複用的定義:

interface MyTypeFn {
  (x: number, y: number): number;
}
let add2:MyTypeFn; 
add2= function(x: number, y: number): number {
  return x + y;
};
add2(1, 2);

5.可索引屬性

  • 與使用接口描述函數類型差不多,我們也可以描述那些能夠“通過索引得到”的類型,比如a[10]ageMap["daniel"]
  • 可索引類型具有一個 索引簽名,它描述了對象索引的類型,還有相應的索引返回值類型

下面接口裏的代碼表示索引是數字, 通過索引訪問對象裏面的值返回數字類型

interface MyIndex {
  [index: number]: number;
}
let arr1: MyIndex;
arr1 = {
  0: 1,
  1: 2
};
let arr2: MyIndex;
arr2 = [2, 3];

arr1和arr2都是正常的,這代表MyIndex接口類型,定義的數據有兩種方式實現,一個是對象一個是數組
下面接口裏的索引是字符串, 通過索引訪問對象裏面的值返回字符串類型

interface MyIndex1 {
  [index: string]: string;
}
let arr2: MyIndex1;
arr2 = {
  '0': 'red',
  '1': 'blue'
};

TypeScript支持兩種索引簽名:字符串和數字。 可以同時使用兩種類型的索引,但是要注意:
數字索引的返回值必須是字符串索引返回值類型的子類型或者相同;因爲obj[100]等同於obj['100'](obj[100]會被自動轉換爲obj['100'])

class Animal {
  name: string;
}
class Dog extends Animal {
  breed: string;
}
let c1 = new Dog();
let c2 = new Animal();
// 錯誤:使用數值型的字符串索引的返回值Animal不是 使用字符串索引y的返回值Dog  的派生類類, 而是基類
interface NotOkay {
   // [Index: number]: Animal;  //number索引的返回值  一定要是 string索引返回值得 派生類或者相同類
   [Index: number]: Dog; 
   [Index: string]: Animal; // 這裏寫Dog也可以, 相同或者基類都是可以的,另外把這裏的Index改爲x其他字符都是可以的,這裏的Index只是起到一個標識的作用
}
let a1: NotOkay = {
  2: c1,
  age: c2
};

6.類接口

類定義會創建兩個東西:類的實例類型和一個構造函數。 因爲類可以創建出類型,所以你能夠在允許使用接口的地方使用類

class Point {
    x: number;
    y: number;
}
interface Point3d extends Point {
    z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};

7.泛型接口

使用泛型接口, 可複用的支持任意傳入參數
和我們之前學過的函數類型接口有點相似

let fn3 = function(x: string, y: string): string[] {
     return [x, y];
};

上面的fn3函數的類型我們沒有定義,是利用的 類型推論自動獲取的,現在使用接口來定義一個符合我們這個函數需要的形狀。並且聲明一個帶類型的函數fn3

interface MyFn {
     (x: string, y:string): string[]
}
let fn3:MyFn;

這個類型再修改一下,增加接口的複用性,將參數string換成動態的,由使用者決定;那麼我們就需要使用泛型

interface MyFn {
     <T>(x: T, y: T):T[]
}
let fn3:MyFn;

到這裏我們的這個函數接口形狀就已經完成,還可以將泛型參數提升到我們的接口名稱上

interface MyFn<T> {
     (x: T, y:T): T[]
}
let fn3:MyFn;

到這裏我們的函數類型就可以傳入任意類型的值,這個接口形狀可複用性就更高了

8.接口繼承接口

和類一樣,接口也可以相互繼承。 這讓我們能夠從一個接口裏複製成員到另一個接口裏,可以更靈活地將接口分割到可重用的模塊裏

interface Animal {
  color: string;
}
interface Dog extends Animal {
  bodyLength: number;
}
let mydog: Dog = <Dog>{};
mydog.color = 'blue';
mydog.bodyLength = 10;

一個接口可以繼承多個接口,創建出多個接口的合成接口

interface Animal {
  color: string;
}
interface Dog {
  bodyLength: number;
}
// angular裏面大量的使用類繼承自多個內置類
interface GreyDog extends Animal, Dog {
  only: string;
}
let mydog: GreyDog = <GreyDog>{};
mydog.color = 'blue';
mydog.bodyLength = 10;
mydog.only = '我的阿黃';

9.接口繼承類

當接口繼承了一個類類型時,它會繼承類的成員但不包括其實現;就好像接口聲明瞭所有類中存在的成員,但並沒有提供具體實現一樣

class Animal {
   name: string;
   // move: (x: string, y: number) => string;
   move() {
       console.log(123);
   }
}
interface Dog extends Animal {
  eat(): void;
}
let d1: Dog = {
  name: '阿黃',
  move() {
    console.log(456);
   },
   // move(x: string, y: number) {
   //   console.log(123);
   //   return x;
   // },
   eat() {
     console.log(123);
   }
 };

10.類實現(implements)接口

TypeScript能夠用它來明確的強制一個類去符合某種契約
實現(implements)
是面向對象中的一個重要概念。一般來講,一個類只能繼承自另一個類,有時候不同類之間可以有一些共有的特性,這時候就可以把特性提取成接口(interfaces),用 implements 關鍵字來實現。這個特性大大提高了面向對象的靈活性; 這個implements在angular裏面也有大量的使用

interface Alarm {
   // 定義一個公用的方法,具體的實現在實現的類裏面去實現
   warning():void;
}
class Door implements Alarm {
   warning() {
     console.log('門報警器');
   }
}
class Car implements Alarm {
   warning() {
     console.log('車報警器');
   }
}
class Baoma extends Car implements Alarm {
   warning() {
     console.log('寶馬車報警器');
   }
}

這個案例裏面 車和門都有報警功能,所以將這個公共的功能抽離出來封裝爲一個接口
需要這個功能的類比如Car Door Baoma 等去實現這個接口 implements即可
要注意的是在接口裏面是方法的簽名,在類裏面進行方法體的實現

let d1 = new Door();
let c1 = new Car();
let b1 = new Baoma();
d1.warning();
b1.warning();

這個打印只有兩個屬性; warning這個方法是綁定在構造函數Baoma的原型對象prototype上面的,可以在瀏覽器裏面查看

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