TypeScript的核心原则之一是对值所具有的结构进行类型检查,而接口就是用来定义值的结构,接口自身当作类型来用。
基本使用
先看一段代码:
function func(obj: { name: string }): void { console.log('hello ' + obj.name) }
很明显,函数func要求参数是一个包含name属性的对象,name值为string类型。而 { name: string } 就可以被定义为接口,如下:
interface objType { // 定义接口 objType name: string } function func(obj: objType): void { console.log('hello ' + obj.name) }
代码里使用 interface 来定义接口,需要注意的是,不要把它理解为是在定义一个对象,而要理解为 {} 括号包裹的是一个代码块,里面是一 条条声明语句,只不过声明的不是变量的值而是类型。上述代码定义的是最简单的接口结构,只有一个属性时定义成接口或许没必要,但当结构复杂时,接口的用处就会慢慢体现。
可选属性
interface objType { name: string age?: number // age 是可选的 } function func(obj: objType): void { let age = obj.age ? obj.age : 18 console.log(obj.name + ' is ' + age + ' years old ') }
只读属性
一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly
来指定只读属性:
interface Point { readonly x: number // 加上readonly前缀,表示只读 readonly y: number // 同上 } let n: Point = { x: 3, y: 5 } n.x = 10 // Cannot assign to 'x' because it is a read-only property.
定义只读变量用 const,定义只读属性用 readonly。
额外属性检查
当某接口类型里不存在某个属性时,就不能应用这个属性,否则会报错,例如:
interface Point { x: number y: number } let n: Point = { x: 3, y: 5, z: 10 } // 这里加入了z属性,会报错 // Type '{ x: number; y: number; z: number; }' is not assignable to type 'Point'. // Object literal may only specify known properties, and 'z' does not exist in type 'Point'.
很多时候,我们希望绕过typescript的这个检查,那么有几个方法需要知道:
(1)类型断言
将值断言为Point类型,告诉typescript我们已经自行检查过了,没有问题
let n: Point = { x: 3, y: 5, z: 10 } as Point
(2)字符串索引签名
定义任意属性,属性名是string类型,propName是泛指的属性名,只要不是 x 和 y,就无所谓它的值类型:
interface Point { x: number y: number [propName: string]: any } let n: Point = { x: 3, y: 5, z: 10 }
(3)利用类型兼容性
这个方法说白了就是先把值赋值给一个变量,然后再把这个变量赋值给这个接口类型的变量:
interface Point { x: number y: number } let obj = { x: 3, y: 5, z: 10 } // 需要的值赋值给变量 let n: Point = obj // 变量赋值给接口类型变量
函数类型接口
除了描述带有属性的普通对象外,接口也可以描述函数类型。为了使用接口表示函数类型,我们需要给接口定义一个调用签名:
interface AddFunc { (num1: number, num2: number): number }
接口AddFunc要求实现这个接口结构的值须包含一个函数,其函数结构和接口内定义的函数结构一致。如:
let func: AddFunc func = function(num1:number, num2:number): number { return num1 + num2 }
对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。 比如,我们使用下面的代码重写上面的例子:
let func: AddFunc func = function(n1:number, n2:number): number { // n1,n2代替num1,num2 return n1 + n2 }
函数的参数会逐个进行检查,要求对应位置上的参数类型是兼容的。另外你也可以不指定类型,由typescript推导类型:
interface AddFunc { (num1: number, num2: number): number } let func: AddFunc func = function(n1, n2) { return n1 + n2 }
可索引的类型接口
接口可以描述那些“可通过索引得到”的类型,如数组 a[1] 或对象 obj["name"],可以同时给索引和值都设置类型,如:
interface MyArray { [index: number]: string } const arr: MyArray = ['Apple', 'Orange'] interface MyObject { [key: number]: string } const obj: MyObject = { 0: 'hello', 1: 'world' }
接口的继承
接口也可以继承,本质上就是从一个接口里复制成员到另一个接口里,更灵活的复用属性。
// 定义一个 Person 接口,它包含两个属性和一个方法 interface Person{ age:number; name:string; say():void; } // 定义一个 Teacher 接口,继承 Person 的属性成员,同时拥有自己的 teach 方法 interface Teacher extends Person{ teach():void; } // 使用接口 const teacher:Teacher = { teach(){}, age:25, name:'tom', say(){} }
混合类型
在 js 中,函数是对象,而对象可以有属性,所以有时一个对象,它既是一个函数,也包含一些属性,如这个计数器函数:
function countUp(){ return ++countUp.count } countUp.count = 0 console.log(countUp()) // 1 console.log(countUp()) // 2
现在来使用混合类型接口来定义上面 countUp 类型:
interface Counter{ ():void; count:number } function getCounter():Counter{ const c = ()=>{ c.count++ } c.count = 0 return c } const counter:Counter = getCounter() counter() console.log(counter.count) counter() console.log(counter.count)
以上就是接口的相关内容,接口中还有涉及类的一些内容,后面写到类相关时再说。