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)
以上就是接口的相关内容,接口中还有涉及类的一些内容,后面写到类相关时再说。