- ts 接口
interface
- 在 ts 中,我们使用接口(Interfaces)来定义对象的类型。
- 在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。
- ts 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。赋值的时候,变量的形状必须和接口的形状保持一致。
- 接口命名规范:一般首字母大写,建议接口的名称加上
I
前缀,表示它是 ts 中的接口类型。 - 定义接口时,每个字段结束可以加一个分号也可以不加分号
interface IPerson {
name: string;
age: number;
}
// ok “形状”一致
let p1: IPerson = {
name: "p1",
age: 1,
};
// error “形状”不一致
let p2: IPerson = {
name: "p2",
};
// error “形状”不一致
let p3: IPerson = {
name: "p3",
age: 3,
gender: "male",
};
- 在接口中描述一个可选字段
<字段> ?: <类型>
interface IPerson {
name: string;
age?: number; // age 可有可无
}
// ok
let p1: IPerson = {
name: "p1",
};
// ok
let p2: IPerson = {
name: "p2",
age: 2
};
// error “形状”不一致
let p3: IPerson = {
name: "p3",
age: 3,
gender: "male",
};
当你去访问一个可选属性时,ts 会提示它是一个联合类型,比如 typeof p1.age
得到的结果是 number | undefined
- 在接口中描述任意属性
[propName: string]: any
- 一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
- 一个接口中只能定义一个任意属性
- 如果接口中有多个类型的属性,则可以在任意属性中使用联合类型
interface IPerson {
name: string
age?: number // age 可有可无
[propName: string]: any
}
// ok
let p1: IPerson = {
name: "p1",
age: 1,
// 以下皆为任意属性 [propName: string]: any
gender: "male",
a: 'a',
bc: 'bc'
};
interface IPerson {
name: string;
// error 类型“number | undefined”的属性“age”不能赋给“string”索引类型“string”。
age?: number;
[propName: string]: string;
}
interface IPerson {
name: string;
// ok
age?: number; // age 可能是 number、undefined
[propName: string]: string | number | undefined;
}
- 只读属性
readonly
- 有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用
readonly
定义只读属性 - 只读属性必须在定义的同时完成初始化,且后续不可更改
interface IPerson {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
let tom: IPerson = {
id: 89757,
name: "Tom",
gender: "male",
};
// error 无法为“id”赋值,因为它是只读属性。
tom.id = 9527;
interface IPerson {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
// error 类型 "{ name: string; gender: string; }" 中缺少属性 "id",但类型 "IPerson" 中需要该属性。
let tom: IPerson = {
name: 'Tom',
gender: 'male'
};
- 【应用场景】接口 vs. 类型别名
interface
用来描述对象、类的结构type
用来描述非对象、类的结构- 以上仅是建议,在很多场景下
interface
、type
可相互替代,可自行选择喜欢的写法,并在工程中一以贯之便是。
- 接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法
- ts 中定义接口的语法
interface <接口名> { ... }
interface IPerson {
firstName: string,
lastName: string,
sayHi: () => string
}
var customer: IPerson = {
firstName: "Tom",
lastName: "Hanks",
sayHi: (): string => { return "Hi there" }
}
console.log("Customer 对象 ")
console.log(customer.firstName)
console.log(customer.lastName)
console.log(customer.sayHi())
var employee: IPerson = {
firstName: "Jim",
lastName: "Blakes",
sayHi: (): string => { return "Hello!!!" }
}
console.log("Employee 对象 ")
console.log(employee.firstName)
console.log(employee.lastName)
console.log(employee.sayHi())
// Customer 对象
// Tom
// Hanks
// Hi there
// Employee 对象
// Jim
// Blakes
// Hello!!!
// 接口
interface RunOptions {
program: string;
// 联合类型
commandline: string[] | string | (() => string);
}
// commandline 是字符串
var options: RunOptions = { program: "test1", commandline: "hello" };
console.log(options.commandline);
// commandline 是字符串数组
options = { program: "test1", commandline: ["Hello", "World"] };
// 类型守卫
if (Array.isArray(options.commandline)) {
console.log(options.commandline[0]);
console.log(options.commandline[1]);
}
// commandline 是一个函数表达式
options = { program: "test1", commandline: () => "**Hello World**" };
// 类型守卫
if (typeof options.commandline === "function") {
console.log(options.commandline());
}
// hello
// Hello
// World
// **Hello World**
interface namelist {
[index: number]: string
}
var list2: namelist = ["Google", "Runoob", "Taobao"] // 类型一致,正确
var list2: namelist = ["Runoob", 1, "Taobao"] // 错误元素 1 不是 string 类型
// 报错:不能将类型“number”分配给类型“string”。ts(2322)
interface ages {
[key: string]: number
}
var agelist: ages = {};
agelist["runoob"] = 15 // 类型正确
agelist[2] = "google" // 类型错误
// 报错:不能将类型“string”分配给类型“number”。ts(2322)
- 接口继承就是说接口可以通过其他接口来扩展自己
- ts 允许接口继承多个接口
- ts 接口继承使用关键字
**extends**
- 接口继承语法
**<子接口> extends <父接口>**
、**<子接口> extends <父接口1>, <父接口2>, ..., <父接口n-1>, <父接口n>**
- 接口不会出现在编译结果中
interface Person {
age: number
}
interface Musician extends Person {
instrument: string
}
var drummer = <Musician>{};
drummer.age = 27
drummer.instrument = "Drums"
console.log("年龄: " + drummer.age)
console.log("喜欢的乐器: " + drummer.instrument)
// 年龄: 27
// 喜欢的乐器: Drums
interface IParent1 {
v1: number
}
interface IParent2 {
v2: number
}
interface Child extends IParent1, IParent2 { }
var Iobj: Child = { v1: 12, v2: 23 }
console.log("value 1: " + Iobj.v1 + " value 2: " + Iobj.v2)
// value 1: 12 value 2: 23