原文地址:https://banggan.github.io/2019/01/21/TypeScript学习笔记之对象类型/
在TypeScript中,利用接口(Interfaces)来定义对象的类型。
在面向对象语言中,接口(Interfaces)、它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implements)。
在TypeScript中没有接口的概念,在编译成JavaScript的时候,所有的接口会被擦出掉,而TypeScript的核心之一就是类型检查。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约并让代码看起来更好理解。
TypeScript中的接口
可用于对对象的形状进行描述,也可用于对类的一部分进行抽象,如:
interface Person {
name:string;
age:number;
}
function put(person:Person){
console.log("hello" + person.name+"I'm"+ person.age);
}
let Tom: Person = { //这里的对象只要包含必要的属性且值类型正确就可以
name: 'Tom',
age: 25
};
如果不给传进去的对象指定是接口类型的数据,那么传入的对象参数可以包含其他属性,编译器会检查必要的属性是否存在且判断类型是否正确。
如果是是指定了接口类型的数据,多属性和少属性均会出错
let Tom: Person = {//两种情况均不允许
name: 'Tom'
};
let Tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
};
可选属性
在实际的开发中,接口里的属性不全是需要的,就可以使用可选属性:
带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号。
//这里name和age均是可选属性,在传对象的时候name和age可有可无
interface Person {
name?: string;
age?: number;
}
function getInfo(p:person){
console.log(p.name);
console.log(p.age);
}
let Tom ={name:'tom'}
let Tom ={age:20}
getInfo(Tom);
任意属性
如果我们希望一个借口有任意的属性,可使用 [propName: string]: any来实现:
interface Person {
name: string;
age?: number;
[propName: string]: any;
}
let Tom: Person = {
name: 'Tom',
gender: 'male'
};
一旦定义了任意属性,则确定和可选属性必须是他的子属性。
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
};
//error TS2411: Property 'age' of type 'number' is not assignable to string index type 'string'.
这里的任意属性是string,而age是number,number不是string的子属性,所以报错
只读属性
如果我们希望对象的属性只能在对象创建时候修改其值,在属性名的前面用readonly来指定只读属性:
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
id: 89757,
name: 'Tom',
gender: 'male'
};
tom.id = 9527;
//error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.
定义只读数组
TypeScript具有ReadonlyArray类型,它与Array相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改:
let a: ReadonlyArray<number> =[1,2,3,4];
a[0] =2;//error
a.length = 9;//error
函数类型
为了使用接口表示函数类型,我们需要给接口定义一个调用签名。 它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
interface getStr{
/* 函数传入两个number类型的参数,返回一个string*/
(x:number,y:number):string;
}
let myStr: getStr;
myStr = function(grade:number,class:nember){
return `${grade}年级${class}班`;
}
console.log(myStr(2,3)); //2年级3班
可索引类型
与使用接口描述函数类型差不多,我们也可以描述那些能够“通过索引得到”的类型,比如a[10]或ageMap[“daniel”]。 可索引类型具有一个 索引签名,它描述了对象索引的类型,还有相应的索引返回值类型
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];//打印Bob
定义了StringArray接口,它具有索引签名。 这个索引签名表示了当用 number去索引StringArray时会得到string类型的返回值
TypeScript支持两种索引签名:字符串和数字。 可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。 这是因为当使用 number来索引时,JavaScript会将它转换成string然后再去索引对象。 也就是说用 100(一个number)去索引等同于使用"100"(一个string)去索引,因此两者需要保持一致。
类类型
一个类去实现接口,而不是直接把接口拿来用
interface Clock{
//定义一个由属性和方法的接口
currentTime:Date;
setTime(d:Date);
}
//Time类实现了Clock接口
class Time implements Clock{
currentTime:Date;
setTime(d:Date){
this.currentTime = d
}
constructor(h: number, m: number) { }//类的静态部分,不会检查
}
继承接口
和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。
//接口可以继承接口 并且可以多继承
interface shape{
color:string;
}
interface pen extends shape{
width:number;
}//创建一个对象并指定泛型
circle.color="red";//这里可以获取color属性
circle.width=2;//有width属性
一个接口继承多个接口,创建多个接口的合成接口
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface pen extends Shape, PenStroke {
sideLength: number;
}
let square = <pen>{}; //创建一个对象并指定泛型
square.color = "blue";
square.sideLength = 10;
混合类型
所谓的混合类型就是在一个接口中定义多种类型,比如属性,函数,数组等:
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;