TypeScript學習筆記之 介面(Interface)
在java中,介面是用來定義一些規範,使用這些介面,就必須實現介面中的方法,而且介面中的屬性必須是常量。
javascript中是沒有介面的概念的。所以TypeScript在編譯成 JavaScript 的時候,所有的介面都會被擦除掉。
而TypeScript的核心之一就是型別檢查。 在TypeScript裡,介面的作用就是為這些型別命名和為你的程式碼或第三方程式碼定義契約並讓程式碼看起來更好理解。
介面的簡單使用
如下程式碼所示,我們需要給add函式中傳入一個物件,如果不使用介面,我們傳入的物件可以有其他屬性。
function add(num:{x:number,y:number}) {
console.log(num.x+num.y);
}
let n={x:1,y:2,z:3};//這裡我們的物件只要包含必要屬相且值型別正確即可,允許存在其他屬性
add(n);//不報錯
// add({x:1,y:2,z:2})//報錯,會提示z不存在
add({x:1,y:2});//不報錯
用介面來表示,可以看到,我們先定義個介面,然後把符合介面定義的物件傳進去,這樣能提高程式碼可讀性。
需要注意的是:
如果我們不給傳進去的物件指定是介面型別的資料,那麼傳入的物件引數可以包含其他屬性,編譯器只會檢查那些必需的屬性是否存在,並且其型別是否匹配。
如果我們給物件指定是介面型別的資料,那麼,物件的屬性必須和定義好的介面的必要屬性一致。必要屬性不能多也不能少。
/**
* Created by yzq on 2017/1/12.
*/
interface num{
x:number;
y:number;
}
function add(n:num){
console.log(n.x+n.y);
}
let n={x:1,y:2,z:3};
let n1:num={x:1,y:2};//如果我們將n1指定為num型別的資料,那麼,該物件所包含的屬性必須跟定義好的介面完全一致。不能多也不能少。
// let n2:num={x:1};//錯誤,缺少y屬性
// let n3:num={x:1,y:2,z:3};//錯誤,沒有找到z屬性,當我們將物件字面量賦值給變數或作為引數傳遞的時候。TypeScript會進行額外屬性檢查。如果一個物件字面量存在任何“目標型別”不包含的屬性時,就會報錯。
add(n);//不報錯
add(n1);//不報錯
add(n2);
add(n3);
// add({x:1,y:2,z:2})//報錯,會提示z不存在,額外的屬性檢查
add({x:1,y:2});//不報錯
介面的可選屬性
在實際應用中,接口裡的屬性不全都是必需的。 有些是隻在某些條件下存在,或者根本不存在。在這種個情況下可以是用介面的可選屬性去定義。
如下程式碼所示,name和age被定義為可選屬性,那麼在傳物件的時候name和age就可有可無。
interface Person{
name?:string;
age?:number
}
function getInfo(p:Person){
console.log(p.name);
console.log(p.age);
}
let student={}
// let student={name:"yzq"}
// let student={name:"yzq",age:23}
getInfo(student);
介面的只讀屬性
如果我們希望物件屬性只能在物件剛剛建立的時候修改其值。 我們可以在屬性名前用 readonly來指定只讀屬性。
interface Point{
readonly x:number;
readonly y:number;
}
function getPoint(p:Point){
console.log(p.x);
console.log(p.y);
}
let point:Point={x:1,y:2};
// point.x=2;//錯誤 這裡不能再子修改值
getPoint(point);
定義只讀陣列
只讀陣列也一樣,一旦定義後不能再修改陣列
let a:ReadonlyArray<number> =[1,2,3,4,5];
// a[0]=2;//不能再修改該陣列
// a.length=20;
介面的函式型別
介面可以描述javascript的任何物件,不僅能描述物件的屬性型別,當然也能描述物件的函式型別。
如下程式碼所示,介面描述了這個函式的引數型別和返回值型別
interface getStr {
/*在這裡我們描述了這個介面有個函式 這個函式傳進去2個number型別的引數 然後返回一個string型別的資料*/
(x: number, y: number): string;
}
let myStr : getStr;
myStr = function (gradeNum: number, classNum: number) {
return `${gradeNum}年級${classNum}班`;
}
console.log(myStr(2,8));//2年級8班
介面的陣列型別(可索引的型別)
跟介面描述函式型別差不多,我們也可以描述那些能夠“通過索引得到”的型別,比如通過下標獲取陣列中的值 a[2];需要注意的是,索引器的型別只能為 number 或者 string。
interface stringArr{
/*描述的一個數組 這個數組裡面的元素是string型別 並且只能通過number型別來索引 [index:number]是索引器 string是該陣列元素型別*/
[index:number]:string;
// age:number;//需要注意的是,當我們將這個介面是陣列型別時,那麼,介面中定義的其它屬性的型別都必須是該陣列的元素型別。 這裡的number型別是報錯的
}
let strArr:stringArr;
strArr=["1","2"];
// strArr.name="3";
let str:string=strArr[0];
console.log(str);//列印1
介面的類型別
所謂類型別,就是一個類去實現介面,而不是直接把介面拿來用,這更符合我們的使用習慣。
interface IClock{
/*定義了一個介面 這個介面中有一個屬性和一個方法*/
currentTime:Date;
getTime(d:Date);
}
/*Time類實現IClock介面*/
class Time implements IClock{
currentTime:Date;
getTime(d:Date){
this.currentTime=d;
}
}
擴充套件介面
在TypeScript中,介面跟類一樣是可以相互繼承的, 這讓我們能夠從一個接口裡複製成員到另一個接口裡,可以更靈活地將介面分割到可重用的模組裡。
/*介面可以繼承介面 並且可以多繼承*/
interface shape{
color:string;
}
interface pen extends shape{
width:number;
}
let circle=<pen>{};//注意這裡的寫法,建立一個物件並指定泛型
circle.color="red";//這裡可以獲取color屬性
circle.width=2;//有width屬性
一個介面可以繼承多個介面,創建出多個介面的合成介面。
/*介面可以繼承介面 並且可以多繼承*/
interface shape{
color:string;
}
interface pen extends shape{
width:number;
}
interface Circle extends shape,pen{
point:number[];
}
let c=<Circle>{};//注意這裡的寫法,建立一個物件並指定泛型
c.point=[1,2];
c.color="red";
c.width=1;
混合型別
所謂的混合型別就是在一個介面中定義多種型別,比如屬性,函式,陣列等。
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;