欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 新车 > TypeScript 的 interface 接口

TypeScript 的 interface 接口

2025/4/16 15:13:51 来源:https://blog.csdn.net/weixin_74009702/article/details/147198417  浏览:    关键词:TypeScript 的 interface 接口

TypeScript 的 interface 接口

简介

interface 是对象的模板,可以看作是一种类型约定,中文译为“接口”。使用了某个模板的对象,就拥有了指定的类型结构。

 
  1. interface Person {
  2. firstName: string;
  3. lastName: string;
  4. age: number;
  5. }

上面示例中,定义了一个接口Person,它指定一个对象模板,拥有三个属性firstNamelastNameage。任何实现这个接口的对象,都必须部署这三个属性,并且必须符合规定的类型。

实现该接口很简单,只要指定它作为对象的类型即可。

 
  1. const p:Person = {
  2. firstName: 'John',
  3. lastName: 'Smith',
  4. age: 25
  5. };

上面示例中,变量p的类型就是接口Person,所以必须符合Person指定的结构。

方括号运算符可以取出 interface 某个属性的类型。

 
  1. interface Foo {
  2. a: string;
  3. }
  4. type A = Foo['a']; // string

上面示例中,Foo['a']返回属性a的类型,所以类型A就是string

interface 可以表示对象的各种语法,它的成员有5种形式。

  • 对象属性
  • 对象的属性索引
  • 对象方法
  • 函数
  • 构造函数

(1)对象属性

  1. interface Point {
  2. x: number;
  3. y: number;
  4. }

上面示例中,xy都是对象的属性,分别使用冒号指定每个属性的类型。

属性之间使用分号或逗号分隔,最后一个属性结尾的分号或逗号可以省略。

如果属性是可选的,就在属性名后面加一个问号。

 
  1. interface Foo {
  2. x?: string;
  3. }

如果属性是只读的,需要加上readonly修饰符。

 
  1. interface A {
  2. readonly a: string;
  3. }

(2)对象的属性索引

 
  1. interface A {
  2. [prop: string]: number;
  3. }

上面示例中,[prop: string]就是属性的字符串索引,表示属性名只要是字符串,都符合类型要求。

属性索引共有stringnumbersymbol三种类型。

一个接口中,最多只能定义一个字符串索引。字符串索引会约束该类型中所有名字为字符串的属性。

 
  1. interface MyObj {
  2. [prop: string]: number;
  3. a: boolean; // 编译错误
  4. }

上面示例中,属性索引指定所有名称为字符串的属性,它们的属性值必须是数值(number)。属性a的值为布尔值就报错了。

属性的数值索引,其实是指定数组的类型。

 
  1. interface A {
  2. [prop: number]: string;
  3. }
  4. const obj:A = ['a', 'b', 'c'];

上面示例中,[prop: number]表示属性名的类型是数值,所以可以用数组对变量obj赋值。

同样的,一个接口中最多只能定义一个数值索引。数值索引会约束所有名称为数值的属性。

如果一个 interface 同时定义了字符串索引和数值索引,那么数值索性必须服从于字符串索引。因为在 JavaScript 中,数值属性名最终是自动转换成字符串属性名。

 
  1. interface A {
  2. [prop: string]: number;
  3. [prop: number]: string; // 报错
  4. }
  5. interface B {
  6. [prop: string]: number;
  7. [prop: number]: number; // 正确
  8. }

上面示例中,数值索引的属性值类型与字符串索引不一致,就会报错。数值索引必须兼容字符串索引的类型声明。

(3)对象的方法

对象的方法共有三种写法。

 
  1. // 写法一
  2. interface A {
  3. f(x: boolean): string;
  4. }
  5. // 写法二
  6. interface B {
  7. f: (x: boolean) => string;
  8. }
  9. // 写法三
  10. interface C {
  11. f: { (x: boolean): string };
  12. }

属性名可以采用表达式,所以下面的写法也是可以的。

 
  1. const f = 'f';
  2. interface A {
  3. [f](x: boolean): string;
  4. }

类型方法可以重载。

 
  1. interface A {
  2. f(): number;
  3. f(x: boolean): boolean;
  4. f(x: string, y: string): string;
  5. }

interface 里面的函数重载,不需要给出实现。但是,由于对象内部定义方法时,无法使用函数重载的语法,所以需要额外在对象外部给出函数方法的实现。

 
  1. interface A {
  2. f(): number;
  3. f(x: boolean): boolean;
  4. f(x: string, y: string): string;
  5. }
  6. function MyFunc(): number;
  7. function MyFunc(x: boolean): boolean;
  8. function MyFunc(x: string, y: string): string;
  9. function MyFunc(
  10. x?:boolean|string, y?:string
  11. ):number|boolean|string {
  12. if (x === undefined && y === undefined) return 1;
  13. if (typeof x === 'boolean' && y === undefined) return true;
  14. if (typeof x === 'string' && typeof y === 'string') return 'hello';
  15. throw new Error('wrong parameters');
  16. }
  17. const a:A = {
  18. f: MyFunc
  19. }

上面示例中,接口A的方法f()有函数重载,需要额外定义一个函数MyFunc()实现这个重载,然后部署接口A的对象a的属性f等于函数MyFunc()就可以了。

(4)函数

interface 也可以用来声明独立的函数。

 
  1. interface Add {
  2. (x:number, y:number): number;
  3. }
  4. const myAdd:Add = (x,y) => x + y;

上面示例中,接口Add声明了一个函数类型。

(5)构造函数

interface 内部可以使用new关键字,表示构造函数。

 
  1. interface ErrorConstructor {
  2. new (message?: string): Error;
  3. }

上面示例中,接口ErrorConstructor内部有new命令,表示它是一个构造函数。

TypeScript 里面,构造函数特指具有constructor属性的类,详见《Class》一章。

interface 的继承

interface 可以继承其他类型,主要有下面几种情况。

interface 继承 interface

interface 可以使用extends关键字,继承其他 interface。

 
  1. interface Shape {
  2. name: string;
  3. }
  4. interface Circle extends Shape {
  5. radius: number;
  6. }

上面示例中,Circle继承了Shape,所以Circle其实有两个属性nameradius。这时,Circle是子接口,Shape是父接口。

extends关键字会从继承的接口里面拷贝属性类型,这样就不必书写重复的属性。

interface 允许多重继承。

 
  1. interface Style {
  2. color: string;
  3. }
  4. interface Shape {
  5. name: string;
  6. }
  7. interface Circle extends Style, Shape {
  8. radius: number;
  9. }

上面示例中,Circle同时继承了StyleShape,所以拥有三个属性colornameradius

多重接口继承,实际上相当于多个父接口的合并。

如果子接口与父接口存在同名属性,那么子接口的属性会覆盖父接口的属性。注意,子接口与父接口的同名属性必须是类型兼容的,不能有冲突,否则会报错。

 
  1. interface Foo {
  2. id: string;
  3. }
  4. interface Bar extends Foo {
  5. id: number; // 报错
  6. }

上面示例中,Bar继承了Foo,但是两者的同名属性id的类型不兼容,导致报错。

多重继承时,如果多个父接口存在同名属性,那么这些同名属性不能有类型冲突,否则会报错。

 
  1. interface Foo {
  2. id: string;
  3. }
  4. interface Bar {
  5. id: number;
  6. }
  7. // 报错
  8. interface Baz extends Foo, Bar {
  9. type: string;
  10. }

上面示例中,Baz同时继承了FooBar,但是后两者的同名属性id有类型冲突,导致报错。

interface 继承 type

interface 可以继承type命令定义的对象类型。

 
  1. type Country = {
  2. name: string;
  3. capital: string;
  4. }
  5. interface CountryWithPop extends Country {
  6. population: number;
  7. }

上面示例中,CountryWithPop继承了type命令定义的Country对象,并且新增了一个population属性。

注意,如果type命令定义的类型不是对象,interface 就无法继承。

interface 继承 class

inteface 还可以继承 class,即继承该类的所有成员。关于 class 的详细解释,参见下一章。

 
  1. class A {
  2. x:string = '';
  3. y():boolean {
  4. return true;
  5. }
  6. }
  7. interface B extends A {
  8. z: number
  9. }

上面示例中,B继承了A,因此B就具有属性xy()z

实现B接口的对象就需要实现这些属性。

 
  1. const b:B = {
  2. x: '',
  3. y: function(){ return true },
  4. z: 123
  5. }

上面示例中,对象b就实现了接口B,而接口B又继承了类A

某些类拥有私有成员和保护成员,interface 可以继承这样的类,但是意义不大。

 
  1. class A {
  2. private x: string = '';
  3. protected y: string = '';
  4. }
  5. interface B extends A {
  6. z: number
  7. }
  8. // 报错
  9. const b:B = { /* ... */ }
  10. // 报错
  11. class C implements B {
  12. // ...
  13. }

上面示例中,A有私有成员和保护成员,B继承了A,但无法用于对象,因为对象不能实现这些成员。这导致B只能用于其他 class,而这时其他 class 与A之间不构成父类和子类的关系,使得xy无法部署。

接口合并

多个同名接口会合并成一个接口。

 
  1. interface Box {
  2. height: number;
  3. width: number;
  4. }
  5. interface Box {
  6. length: number;
  7. }

上面示例中,两个Box接口会合并成一个接口,同时有heightwidthlength三个属性。

这样的设计主要是为了兼容 JavaScript 的行为。JavaScript 开发者常常对全局对象或者外部库,添加自己的属性和方法。那么,只要使用 interface 给出这些自定义属性和方法的类型,就能自动跟原始的 interface 合并,使得扩展外部类型非常方便。

举例来说,Web 网页开发经常会对windows对象和document对象添加自定义属性,但是 TypeScript 会报错,因为原始定义没有这些属性。解决方法就是把自定义属性写成 interface,合并进原始定义。

 
  1. interface Document {
  2. foo: string;
  3. }
  4. document.foo = 'hello';

上面示例中,接口Document增加了一个自定义属性foo,从而就可以在document对象上使用自定义属性。

同名接口合并时,同一个属性如果有多个类型声明,彼此不能有类型冲突。

 
  1. interface A {
  2. a: number;
  3. }
  4. interface A {
  5. a: string; // 报错
  6. }

上面示例中,接口A的属性a有两个类型声明,彼此是冲突的,导致报错。

同名接口合并时,如果同名方法有不同的类型声明,那么会发生函数重载。而且,后面的定义比前面的定义具有更高的优先级。

 
  1. interface Cloner {
  2. clone(animal: Animal): Animal;
  3. }
  4. interface Cloner {
  5. clone(animal: Sheep): Sheep;
  6. }
  7. interface Cloner {
  8. clone(animal: Dog): Dog;
  9. clone(animal: Cat): Cat;
  10. }
  11. // 等同于
  12. interface Cloner {
  13. clone(animal: Dog): Dog;
  14. clone(animal: Cat): Cat;
  15. clone(animal: Sheep): Sheep;
  16. clone(animal: Animal): Animal;
  17. }

上面示例中,clone()方法有不同的类型声明,会发生函数重载。这时,越靠后的定义,优先级越高,排在函数重载的越前面。比如,clone(animal: Animal)是最先出现的类型声明,就排在函数重载的最后,属于clone()函数最后匹配的类型。

这个规则有一个例外。同名方法之中,如果有一个参数是字面量类型,字面量类型有更高的优先级。

 
  1. interface A {
  2. f(x:'foo'): boolean;
  3. }
  4. interface A {
  5. f(x:any): void;
  6. }
  7. // 等同于
  8. interface A {
  9. f(x:'foo'): boolean;
  10. f(x:any): void;
  11. }

上面示例中,f()方法有一个类型声明是,参数x是字面量类型,这个类型声明的优先级最高,会排在函数重载的最前面。

一个实际的例子是 Document 对象的createElement()方法,它会根据参数的不同,而生成不同的 HTML 节点对象。

 
  1. interface Document {
  2. createElement(tagName: any): Element;
  3. }
  4. interface Document {
  5. createElement(tagName: "div"): HTMLDivElement;
  6. createElement(tagName: "span"): HTMLSpanElement;
  7. }
  8. interface Document {
  9. createElement(tagName: string): HTMLElement;
  10. createElement(tagName: "canvas"): HTMLCanvasElement;
  11. }
  12. // 等同于
  13. interface Document {
  14. createElement(tagName: "canvas"): HTMLCanvasElement;
  15. createElement(tagName: "div"): HTMLDivElement;
  16. createElement(tagName: "span"): HTMLSpanElement;
  17. createElement(tagName: string): HTMLElement;
  18. createElement(tagName: any): Element;
  19. }

上面示例中,createElement()方法的函数重载,参数为字面量的类型声明会排到最前面,返回具体的 HTML 节点对象。类型越不具体的参数,排在越后面,返回通用的 HTML 节点对象。

如果两个 interface 组成的联合类型存在同名属性,那么该属性的类型也是联合类型。

 
  1. interface Circle {
  2. area: bigint;
  3. }
  4. interface Rectangle {
  5. area: number;
  6. }
  7. declare const s: Circle | Rectangle;
  8. s.area; // bigint | number

上面示例中,接口CircleRectangle组成一个联合类型Circle | Rectangle。因此,这个联合类型的同名属性area,也是一个联合类型。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词