TS Interface(接口)

article/2025/9/22 22:17:29

接口(Interface)

  • 用来建立某种代码约定,使得其它开发者在调用某个方法或者创建新的类时,必须遵循接口所定义的代码约定
    • 接口的前面加了一个 I 字母
  • 规范
    • 在代码设计中,接口是一种规范;
    • 接口通常用于来定义某种规范, 类似于你必须遵守的协议,
    • 站在程序角度上说接口只规定了类里必须提供的属性和方法,
    • 从而分离了规范和实现,增强了系统的可拓展性和可维护性;

示例

const getUserInfo = function (user) {return `name: ${user.name}, age: ${user.age}`;
};getUserInfo({ name: 'koala', age: 18 });// 错误的调用
getUserInfo(); // Uncaught TypeError: Cannot read property 'name' of undefined
console.log(getUserInfo({ name: 'long' })); // name: long, age: undefined
getUserInfo({ name: 'long', height: 1.66 }); // name: long, age: undefined
  • 如果这个 getUserInfo 在多人开发过程中,
  • 如果它是个公共函数,多个开发者都会调用,
  • 如果不是每个人点进来看函数对应注释,就可能出现调用问题

改版1

const getUserInfo = (user: { name: string, age: number }): string => {return `name: ${user.name} age: ${user.age}`;
};
getUserInfo({ name: 'long', age: 18 });// 错误的调用
// getUserInfo(); // 错误信息:An argument for 'user' was not provided.
// getUserInfo({ name: 'long' }); // 错误信息:Property 'age' is missing in type '{ name: string; }'
// getUserInfo({ name: 'long', height: 1.88 }); // 错误信息:类型不匹配
  • 调用的时候,会直接提示错误

重构1

  // 先定义一个接口interface IUser {name: string;age: number;}const getUserInfo = (user: IUser): string => {return`name: ${user.name}, age: ${user.age}`;};// 正确的调用getUserInfo({name: "long", age: 18});

重构2

  type IUserInfoFunc = (user: IUser) =>string;interface IUser {name: string;age: number;}const getUserInfo: IUserInfoFunc = (user) => {return`name: ${user.name}, age: ${user.age}`;};getUserInfo({name: "koala", age: 18});//  getUserInfo();

接口的实现

// 接口的实现,使用 implements 关键字interface Entity {title: string;log(): void;}class Post implements Entity {title: string;constructor(title: string) {this.title = title;}log(): void {console.log(this.title);}}

作为参数的类型声明,自定义一个类型.

// 声明一个接口interface IPerson {name: string;age: number;
}// type类似interface,以下写法等同用interface声明IPerson
type IPerson {name: string;age: number;
}class Person {constructor(public config: IPerson) {}
}// 再调用时,要满足接口的要求(不能增加,也不能减少)
var p1 = new Person({name: "张三";age: 18
})// interface 能够声明合并,Type不可以interface User {name: stringage: number
}interface User {sex: string
}/*
User 接口为 {name: stringage: numbersex: string
}
*/

函数类型

interface Animal {eat(food:string);
}class Sheep implements Animal {// 实现接口,必须要实现接口里的类eat(food:string) {console.log("wo chi cao");}
}interface Func {(x: number, y: number, desc?: string): void
}
<=>
type Func = (x: number, y: number, desc?: string) =>void;const sum: Func = function (x, y, desc = '') {
// const sum: Func = function (x: number, y: number, desc: string): void {// ts类型系统默认推论可以不必书写上述类型定义console.log(desc, x + y)
}
sum(32, 22)// 定义函数interface SearchFunc {(source: string, subString: string): boolean;}let mySearch: SearchFunc;// 该函数拥有和类型声明一致的参数类型和返回值。mySearch = function (source: string, subString: string) {let result = source.search(subString);return result > -1;};// 编译器会自动推断参数和返回值的类型let mySearch: SearchFunc;mySearch = function (src, sub) {let result = src.search(sub);return result > -1;};// 编译器推断返回值为 boolean 类型。// 如果你返回了错误的类型,编译器会报错let mySearch: SearchFunc;mySearch = function (src, sub) {// Type '(src: string, sub: string) => string' is not assignable to type 'SearchFunc'.//  Type 'string' is not assignable to type 'boolean'.let result = src.search(sub);return 'string';};

可选属性

  • 可有可无的,那么为什么还要定义呢?
    • 对比起完全不定义,定义可选属性主要是
      • 为了让接口更加的灵活,某些属性我们可能希望设计成可选,
      • 并且如果存在属性,能约束类型,而这也是十分关键的

增任意属性

insterface IPerson {name: string;age ?: number; // 可选属性sex ?: '男' | '女' // IPerson可选一个sex属性,值为'男'或者'女'或者undefined}[propName: string]: any; // 字符串类型的索引签名
}let user: IPerson = {name: 'mark',age: 1,work: 'web'
};// 如果任意类型定义为string 那么 上面代码会报错。
insterface IPerson {name: string;age ?: number;[propName: string]: string; //error TS2411: Property 'age' of type 'number' is not assignable to string index type 'string'
}// 可选属性,使用,先进行判断后,进行使用
if (user.age) {user.age
}

多余属性检查

interface SquareConfig {color?: string;width?: number;
}function createSquare(config: SquareConfig): { color: string; area: number } {return { color: config.color || "red", area: config.width || 20 };}// let mySquare = createSquare({ colour: "red", width: 100 }); // 报错// 报错原因 // TypeScript 认为这可能是一个 BUG。// 编译器在将对象字面量赋值给别的变量或者传递给某个函数时会有一个被称为***多余属性检查***的特殊过程。// 如果对象字面量有任何目标类型所没有的属性时,就会报错;// Argument of type '{ colour: string; width: number; }' is not assignable to parameter of type 'SquareConfig'.//  Object literal may only specify known properties, but 'colour' does not exist in type 'SquareConfig'. Did you mean to write ‘color'// 跳过该检查// let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);// 如果非要 增加额外属性// 添加一个字符串类型的索引签名interface SquareConfig {color?: string;width?: number;[propName: string]: any;}

可索引的类型

// 接口除了可以给函数、对象声明类型,
// 还可以给任何有索引的对象声明类型,比如数组。
// 可索引的类型由索引签名来描述对象的类型interface StringArray {[index: number]: string;}// StringArray 拥有一个索引签名,这个签名的属性的类型是 number,而属性值的类型是 stringlet myArray: StringArray;myArray = ["Bob", "Fred"];let myStr: string = myArray[0];// 索引签名的属性的类型有两种,number 或者 string;// 当然你可以同时支持两种类型,// 但是使用数字来索引的属性的类型必须是使用字符串索引的类型的子类型,// 因为 JavaScript 在索引的时候,// 会将数字类型的属性转换为字符串类型。// 这就意味着索引 100 的类型应该和索引 "100" 的一致interface Animal {name: string;}interface Dog extends Animal {breed: string;}// Error: indexing with a numeric string might get you a completely separate type of Animal!interface NotOkay {// [x: number]: Animal;// Numeric index type 'Animal' is not assignable to string index type 'Dog'.[x: number]: Dog; // [x:number]:Dog , Dog 要和 Dog保持一致[x: string]: Dog;}// 只读类型的 可索引类型interface ReadonlyStringArray {readonly [index: number]: string;}let myArray: ReadonlyStringArray = ['Alice', 'Bob'];// myArray[2] = 'Mallory'; // error!// Index signature in type 'ReadonlyStringArray' only permits reading.

只读属性控制

interface IPerson {readonly id: number;name: string;age?: number;[index: number]: number; // 数组family: Array<string> // IPerson需要包含一个family属性,类型是数组,数组里面都是string类型的数据[propName: string]: any;
}let user: IPerson = {id: 1,name: 'mar;',work: ‘web’,family:[‘1’]
};
user.id = 2;
// 报错 Cannot assign to 'id' because it is a constant or a read-only property.// 只读数组let a: number[] = [1, 2, 3, 4];let ro: Readonly Array<number> = a;// ro[0] = 12; // error!// Index signature in type 'readonly number[]' only permits reading.// ro.push(5); // error!// Property 'push' does not exist on type 'readonly number[]'.// ro.length = 100; // error!// Cannot assign to 'length' because it is a read-only property.// a = ro; // error!// The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.// 通过类型断言来跳过该问题let a: number[] = [1, 2, 3, 4];let ro: Readonly Array<number> = a;a = ro as number[];

类类型

interface ClockInterface {currentTime: Date;
}class Clock implements ClockInterface {currentTime: Date = new Date();constructor(h: number, m: number) {}}// 定义方法interface ClockInterface {currentTime: Date;setTime(d: Date): void;}class Clock implements ClockInterface {currentTime: Date = new Date();setTime(d: Date) {this.currentTime = d;}constructor(h: number, m: number) {}}// 接口只能定义类的公开部分(公开属性、公开方法)// 而不能定义私有部分,因此你不能通过接口来约束类的私有实现

实例接口和静态接口

  • 类有两种类型:
    • 实例的类型
    • 静态的构造函数的类型。
// 如果你用构造签名声明一个接口再让一个类去实现的话,就会报错interface ClockConstructor {new (hour: number, minute: number);
}
// 报错
class Clock implements ClockConstructor {// Class 'Clock' incorrectly implements interface 'ClockConstructor'.//  Type 'Clock' provides no match for the signature 'new (hour: number, minute: number): any'.currentTime: Date;constructor(h: number, m: number) {}
}
// 原因 当类实现一个接口的时候,类实现的是实例的接口(而非构造函数的)
  // 定义了两个接口,// 构造函数类型 ClockConstructor 和实例类型 ClockInterface,// 再定义了一个创建实例的函数 createClock,该函数返回第一个参数的实例// 构造函数类型接口 ClockConstructorinterface ClockConstructor {new (hour: number, minute: number): ClockInterface;}// 实例类型 ClockInterfaceinterface ClockInterface {tick(): void;}/*** @description: 创建实例函数* @param {type} ctor:ClockConstructor* @return: ClockInterface的实例*/function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {return new ctor(hour, minute);}class DigitalClock implements ClockInterface {constructor(h: number, m: number) {}tick() {console.log('beep beep');}}class AnalogClock implements ClockInterface {constructor(h: number, m: number) {}tick() {console.log('tick tock');}}let digital = createClock(DigitalClock, 12, 17);let analog = createClock(AnalogClock, 7, 32);// createClock 的第一个参数应该拥有 ClockConstructor 类型,// 因此该函数会检查传递进来的 DigitalClock 和 AnalogClock 的构造函数是否是 ClockConstructor 类型
// 两个类型结合起来,就是通过类表达式(而非类声明)interface ClockConstructor {new (hour: number, minute: number);}interface ClockInterface {tick();}const Clock: ClockConstructor = class Clock implements ClockInterface {constructor(h: number, m: number) {}tick() {console.log('beep beep');}};// const Clock: ClockConstructor 声明了类的构造函数类型,// 而后面的 class Clock implements ClockInterface 则声明了类的实例类型。

继承

interface interfaceA {a:number;
}
interface interfaceB {b:string;
}
interface interfaceC extends interfaceA,interfaceB {c:boolean;
}let interC:interfaceC = <interfaceC>{};// 定义的同名属性的类型不同的话,是不能编译通过的interface Shape {color: string;test: number; // <-
}interface PenStroke extends Shape{penWidth: number;test: string; // <-
}

混合类型

interface Counter {(start: number): string;interval: number;reset(): void;}function getCounter(): Counter {let counter = function (start: number) {} as Counter;counter.interval = 123;counter.reset = function () {};return counter;}let c = getCounter();c(10);c.reset();c.interval = 5.0;

继承自类的接口

// 当一个接口继承自类时,
// 这个接口就继承了类的所有成员和对应的类型,但是不会继承实现。
// 继承自类的接口甚至会继承类的 private 和 protected 属性,
// 因此一个继承自类的接口,只能由被继承的类或者其子类实现class Control {private state: any;}interface SelectableControl extends Control {select(): void;}class Button extends Control implements SelectableControl {select() {}}class TextBox extends Control {select() {}}class ImageControl implements SelectableControl {// Class 'ImageControl' incorrectly implements interface 'SelectableControl'.// Types have separate declarations of a private property 'state'.private state: any;select() {}}// SelectableControl 继承了 Control 的所有成员,包括 private 属性 state,// 因此能实现 SelectableControl 接口的类一定是继承自 Control 或其子类的类,// 因为只有 Control 中的 state 才是 SelectableControl 中定义的 state 的原始实现(即使在继承类中实现了 state 也不行,因为 state 是 Control 私有的)。// 实现了 SelectableControl 接口的实例可以通过 Control 访问私有属性 state。// 事实上,SelectableControl 就像 Control 有 select() 方法一样。// Button 和 TextBox 是 SelectableControl 的子类,因为他们继承自 Control 且拥有 select() 方法,// 而 ImageControl 有自己的 private 属性 state 而不是从 Control 继承,因此它就无法实现 SelectableControl 接口了

Notion – The all-in-one workspace for your notes, tasks, wikis, and databases.https://serious-lose.notion.site/TS-Interface-439d14dd883148efbc23929fb1a7647d


http://chatgpt.dhexx.cn/article/thNT08Fe.shtml

相关文章

敏捷项目管理实战第一天 敏捷开发SCURM的前世今生

开篇词 敏捷是互联网时代的超级管理术 你好&#xff0c;我是莫敏。自 2006 年开始接触敏捷&#xff0c;到 2010 年参与组织每年一届的敏捷大会&#xff0c;再到 2012 年加入腾讯先后从事项目管理和产品管理工作&#xff0c;可以说从过去到现在&#xff0c;我一直身处敏捷实践的…

机器人算法之敏捷开发

0. 简介 在开发大型的机器人工程时候&#xff0c;我们会发现团体开发以及代码的review的会非常重要。而这些离不开敏捷开发&#xff08;Scrum&#xff09;以及Git管理。而最常用敏捷开发流程就是DoD。本文也将介绍和学习这种方式&#xff0c;来辅助各位能够在实验室和工作中团…

何谓敏捷开发

敏捷开发&#xff08;agile development&#xff09;是非常流行的软件开发方法。据统计&#xff0c;2018年90%的软件开发采用敏捷开发。 但是&#xff0c;到底什么是敏捷开发&#xff0c;能说清的人却不多。本文尝试用简洁易懂的语言&#xff0c;解释敏捷开发。 一、迭代开发 …

敏捷开发思想

敏捷开发思想 SCRUM 是什么?敏捷开发是什么&#xff1f;以人为核心是什么意思&#xff1f;迭代 是什么意思&#xff1f; SCRUM 与 敏捷开发思想有什么关系&#xff1f;敏捷开发的模式分类(摘抄至互联网)&#xff1a;SCRUM 的工作流程(摘抄至互联网) 流程&#xff1a; SCRUM 是…

敏捷开发-Scrum过程模型

Scrum过程模型 Scrum过程&#xff1a; Scrum的三个主要元素&#xff1a;角色&#xff08;role&#xff09;、活动&#xff08;activity&#xff09;、产出物&#xff08;artifact&#xff09; 三个角色&#xff1a; 1. Product Owner&#xff08;PO&#xff09; 定义产品需求…

瀑布开发与敏捷开发的区别,以及从瀑布转型敏捷项目管理的5大注意事项

事实证明&#xff0c;瀑布开发管理模式并不适合所有的软件项目&#xff0c;但敏捷项目管理却对大多数项目有效。那么当团队选择转型敏捷的时候有哪些因素必须注意&#xff1f; 敏捷开发最早使用者大多是小型、独立的团队&#xff0c;他们通常致力于小型、独立的项目。正是他们的…

敏捷开发流程简介

最小可行化产品 硅谷创业家 Eric Rise 在其著作 《精益创业》 一书中提出了 “精益创业”&#xff08;Lean Startup&#xff09;的理念&#xff0c;其核心思想是&#xff0c;开发产品时先做出一个简单的原型——最小化可行产品&#xff0c;然后通过测试并收集用户的反馈&#…

浅谈敏捷开发中的设计

敏捷开发在当今业界已经大行其道&#xff0c;想要快速交付&#xff0c;采用敏捷开发方法似乎是最好的方式&#xff0c;是否必须要用这就另当别论了。敏捷开发以用户的需求进化为核心&#xff0c;采用迭代、循序渐进的方法进行软件开发&#xff0c;不过&#xff0c;想要真正做到…

什么是敏捷开发?敏捷开发流程的8个步骤

文章目录 一、什么是敏捷开发&#xff1f;二、敏捷开发模式的分类三、SCRUM 的工作流程四、敏捷开发流程的8个步骤包括&#xff1a;五、敏捷开发模型 一、什么是敏捷开发&#xff1f; 敏捷开发&#xff08;Agile&#xff09;是一种以人为核心、迭代、循序渐进的开发方法。 在…

什么是敏捷开发?教你正确理解敏捷开发

敏捷开发是相对于瀑布开发来说&#xff0c;一种轻量级的软件开发方式。敏捷开发是为了快速响应需求变化、通过组建跨职能团队实现持续不断的交付高质量的产品的方法的集合。所有符合敏捷宣言和敏捷开发十二项原则的方法都可以是敏捷开发的一种实践。 在大多数的敏捷开发实践过…

什么是敏捷开发?

什么是敏捷开发&#xff1f; 敏捷开发是一种以人为核心&#xff0c;迭代&#xff0c;循序渐进的开发方式。在敏捷开发中&#xff0c;软件项目的构建被切分成多个子项目&#xff0c;各个子项目的成果都经过测试&#xff0c;具备集成和可运行的特征。加单的说&#xff0c;敏捷开…

GridView 激发了未处理的事件“PageIndexChanging”

在手动给gridview邦定数据源时&#xff0c;会出现这种情况 运行后直接显示分页的1,只有1显示正常,如果点选其它的,比如2或者4什么其它别的,提示: GridView“XXX”激发了未处理的事件“PageIndexChanging”。 手动分页必须有PageIndexChanging事件&#xff0c;添加PageI…

PageView的设置

1、创建一个PageView控件&#xff0c;自动生成background精灵和mask的view视图和indicator View下面有一个content&#xff08;Layout类型&#xff09; &#xff0c;content可以存放每页内容 页面指示器&#xff0c;可以清晰看当前是多少也 2、监听PageView事件 // 监听事件 o…

ViewPager和PageAdapter,FragmentPageAdapter,FragmentStatePageFragment

【Android】ViewPager深入解析&#xff08;一&#xff09; http://www.imooc.com/article/2580 2015-12-07 11:59:28 11830浏览 19评论 话说小伙伴们在使用App的时候有没有注意到很多App的首页都是可以左右滑动的页面呢&#xff1f;很多App还有绚丽的轮播图广告。那么如何实现这…

viewpager.setcurrentitem导致的ANR

参考&#xff1a;Java线程Dump分析工具–jstack dump 文件里&#xff0c;值得关注的线程状态有&#xff1a; 死锁&#xff0c;Deadlock&#xff08;重点关注&#xff09; 执行中&#xff0c;Runnable 等待资源&#xff0c;Waiting on condition&#xff08;重点关注&#x…

对GridView、DetailsView 和 FormView 控件分页属性的PagerSettings类的设置

如果想让分页导航加上图片形式或比如上一页下一页,这种样式,那就要在<GridView>写在这里面</GridView>控件中加上<PagerSettings></PagerSettings>,例如:<PagerSettings Mode="NextPreviousFirstLast" FirstPageText="首页"…

ViewPager + GridView实现GridView分页

概述 通过ViewPager实现GridView的分页 实现 ViewPager通过设置PagerAdapter实现分页。每一页的布局是一个GridView。GridView通过设置自己adapter渲染GridView。 ViewPager&#xff1a;分页器。 GridViewPageAdapter&#xff1a;继承自PagerAdapter。ViewPager的适配器。 Gr…

非常实用的GridView.PagerTemplate 属性

获取或设置 GridView 控件中页导航行的自定义内容。 命名空间:System.Web.UI.WebControls程序集:System.Web&#xff08;在 system.web.dll 中&#xff09; 语法&#xff1a;C# [TemplateContainerAttribute( typeof (GridViewRow))] public virtual ITemplate PagerTempla…

ViewPager的PagerAdapter中的notifyDataSetChanged更新数据总结

最近在工作中遇到了一个问题&#xff0c;就是在viewpager中调用pageradapter.notifydatasetchanged方法&#xff0c;好像没有任何效果&#xff0c;相应的view也没有更新数据&#xff0c;根据官方API是这样解释的&#xff1a;大概是说明Adapter会自动管辖ViewPager每一页(Item)的…