js中的对象是十分常见的数据类型,在前面的章节提到过的object
类型就能够用来表示对象类型,但是使用object
时并没有很好的类型检查和属性提示,在ts中有类型interfce
用来定义对象的类型。
interface
的功能是对对象进行结构规范和类型检查,官网解释是focuses on the shape that values have
。
interface SelfIntro {
name: string;
age: number;
location: {
city: string;
post: string;
};
}
const me: SelfIntro = {
name: "me",
age: 12,
location: {
city: "chengdu",
post: "000000",
},
};
在上面例子中,我们定义了一个名为SelfIntro
的interface
,并规定了该接口有哪些字段以及字段类型,当我们实际使用该结构声明变量时,必须严格按照接口定义赋值。
一旦使用interface
定义的变量,编辑器就能提供很好的属性提示,就像在写静态语言一样。
可选属性
有时候,并不是接口中的所有属性都需要,此时可以用可选属性来表示非必需的属性,使用方式很简单,在属性名的结尾加上?
。
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
let newSquare = { color: "white", area: 100 };
if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({ color: "black" });
使用可选属性的类型相当于是它自身类型和undefined
所组成的联合类型(联合类型对此有更详细的说明)。
虽然可选属性被推导为自身类型和undefined
所组成的联合类型,但是它和真正的联合类型还是有所不同。
interface Person {
name?: string;
age: number | undefined;
}
// 此时ts会报错:Property 'age' is missing in type '{}' but required in type 'Person'.
// 但是对于可选类型的name则不会提示错误
const p: Person = {};
只读属性
如果我们期望对象的属性在被创建后就不能在更改,此时就可以用只读属性,即在属性名前加上readonly
。
interface User {
readonly id: number;
name: string;
}
const userVal: User = { id: 1, name: "xxx" };
如果后续修改id
属性,将会报错。
函数接口
interface
除了能描述对象类型,也能描述函数结构(函数章节对此有更详细的说明)。
interface LogFn {
(message: string): void;
}
// 此时的message会被自动推断为string类型
const fn: LogFn = (message) => {
console.log(message);
};
如果接口中某个属性是函数,则可以表示为:
interface Data {
fn(config: any): void;
}
// 或者箭头形式
interface Data {
fn: (config: any) => void;
}
前端大名鼎鼎的库axios,我们可以直接调用axios,如axios({url:"xxxx"})
;或者当做对象,使用某个属性,如axios.get("xxxx")
。那么它的接口可以这样定义:
interface Axios {
(config: any): Promise<any>;
get(url: string): Promise<any>;
post(url: string): Promise<any>;
}
可索引属性
数组可以使用[]
形式访问下标,如a[10]
。接口可用可索引属性实现相同的效果。可索引属性形如[key:string | number]: any
。
interface StringArray {
[index: number]: string;
}
const myArray: StringArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
前面提到的interface
规范对象结构,并且赋值时,要和所声明的结构一一对应,不能多也不能少。但是有时,除了必需属性,我们希望接口还能支持其他任意符合规范的字段,用可索引属性可以做到。
假如,有一个接口用来描述淘宝收货地址,默认有一个主地址,但是其他收货地址并不确定是多少个。
interface TaobaoAddress {
primaryAddress: string;
[name: string]: string;
}
const address: TaobaoAddress = { primaryAddress: "xxxx" };
address["secondAddress"] = "xxxxx";
address["thirdAddress"] = "xxxxx";
可索引属性的定义的key
类型只能是string
或者number
。
当为string
时, 必须兼容已经定义的其他所有属性类型。如上述的TaobaoAddress
有个string
类型的primaryAddress
,所以可索引属性必须兼容string
类型,如果可索引属性定义为[name: string]: number
则会报错。
当为number
时,只需兼容已经定义的其他属性名为number的类型:
interface NewType {
b: number;
c?: string;
1: boolean; // 1 需要被可索引属性兼容
[key: number]: boolean;
}
const a: NewType = {
b: 1,
c: "str",
1: true,
2: true,
};
接口继承
在面向对象的语言中,子类可以继承父类的属性。接口也继承另一个接口。通过extends
关键字。并且可以多继承。
interface Animal {
lifetime: number;
}
interface Human extends Animal {
name: string;
}
const human: Human = { lifetime: 100, name: "me" };
// 可继承多个接口
interface RunFn {
run(): void;
}
interface Runner extends Human, RunFn {}
const runner: Runner = {
lifetime: 100,
name: "me",
run() {
console.log("running man");
},
};