在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
接口带来了什么好处好处One —— 过去我们写 JavaScriptJavaScript 中定义一个函数,用来获取一个用户的姓名和年龄的字符串: const getUserInfo = function(user) { return name: ${user.name}, age: ${user.age} }
函数调用: getUserInfo({name: "koala", age: 18})
这对于我们之前在写 JavaScript 的时候,再正常不过了,但是如果这个 // 错误的调用 getUserInfo() // Uncaught TypeError: Cannot read property 'name' of undefined console.log(getUserInfo({name: "kaola"})) // name: kaola, age: undefined getUserInfo({name: "kaola", height: 1.66}) // name: koala, age: undefined
JavaScript 是弱类型的语言,所以并不会对我们传入的代码进行任何的检测,有些错你自己都说不清楚,但是就出了问题。 TypeScript 中的 interface 可以解决这个问题const getUserInfo = (user: {name: string, age: number}): string => { return `name: ${user.name} age: ${user.age}`; };
正确的调用是如下的方式: getUserInfo({name: "kaola", age: 18});
如果调用者出现了错误的调用,那么 // 错误的调用 getUserInfo(); // 错误信息:An argument for 'user' was not provided. getUserInfo({name: "coderwhy"}); // 错误信息:Property 'age' is missing in type '{ name: string; }' getUserInfo({name: "coderwhy", height: 1.88}); // 错误信息:类型不匹配
这时候你会发现这段代码还是有点长,代码不便与阅读,这时候就体现了 使用 interface 对 user 的类型进行重构。 我们先定义一个 // 先定义一个接口 interface IUser { name: string; age: number; }
接下来我们看一下 const getUserInfo = (user: IUser): string => { return `name: ${user.name}, age: ${user.age}`; }; // 正确的调用 getUserInfo({name: "koala", age: 18});
// 错误的调用和之前一样,报错信息也相同不再说明。 接口中函数的定义再次改造 定义两个接口: 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();
好处TWO —— 过去我们用 Node.js 写后端接口其实这个说明和上面类似,我再提一下,就是想证明 TypeScript 确实挺香的! 写一个后端接口,我要特意封装一个工具类,来检测前端给我传递过来的参数,比如下图中的
const goodParams: IGoodsBody = this.ctx.body;
而 // -- 查询列表时候使用的接口 interface IQuery { page: number; rows: number; disabledPage?: boolean; // 是否禁用分页,true将会忽略`page`和`rows`参数 } // - 商品 export interface IGoodsQuery extends Query { isOnline?: string | number; // 是否出售中的商品 goodsNo?: string; // 商品编号 goodsName?: string; // 商品名称 } 接口的基础篇接口的定义和 java 语言相同,TypeScript 中定义接口也是使用 interface 关键字来定义: interface IQuery {
page: number;
}
你会发现我都在接口的前面加了一个 接口中定义方法看上面的接口中,我们定义了 interface IQuery { page: number; findOne(): void; findAll(): void; }
如果我们有一个对象是该接口类型,那么必须包含对应的属性和方法(无可选属性情况): const q: IQuery = { page: 1, findOne() { console.log("findOne"); }, findAll() { console.log("findAll"); }, };
接口中定义属性普通属性上面的 可选属性默认情况下一个变量(对象)是对应的接口类型,那么这个变量(对象)必须实现接口中所有的属性和方法。 但是,开发中为了让接口更加的灵活,某些属性我们可能希望设计成可选的(想实现可以实现,不想实现也没有关系),这个时候就可以使用 interface IQuery { page: number; findOne(): void; findAll(): void; isOnline?: string | number; // 是否出售中的商品 delete?(): void }
上面的代码中,我们增加了
const q: IQuery = { page: 1, findOne() { console.log("findOne"); }, findAll() { console.log("findAll"); }, }; console.log(p.isOnline); // undefined p.delete(); // 不能调用可能是“未定义”的对象。
正确的调用方式如下: if (p.delete) { p.delete(); }
大家可能会问既然是可选属性,可有可无的,那么为什么还要定义呢?对比起完全不定义,定义可选属性主要是:为了让 只读属性默认情况下,接口中定义的属性可读可写: 但是有一个关键字 interface IQuery { readonly page: number; findOne(): void; }
给 const q: IQuery = { page: 1, findOne() { console.log("findOne"); }, }; q.page = 10;// Cannot assign to 'page' because it is a read-only property.
接口的高级篇函数类型接口Interface 还可以用来规范函数的形状。Interface 里面需要列出参数列表返回值类型的函数定义。写法如下:
interface Func { // ✔️ 定于这个函数接收两个必选参数都是 number 类型,以及一个可选的字符串参数 desc,这个函数不返回任何值 (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)
注意:不过上面的接口中只有一个函数,TypeScript 会给我们一个建议,可以使用 type Func = (x: number, y: number, desc?: string) => void;
接口的实现接口除了定义某种 下面的代码中会有关于修饰符的警告,暂时忽略,后面详细讲解 // 定义一个实体接口 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 Shape { //定义接口Shape color: string; } interface Square extends Shape { //继承接口Shape sideLength: number; }
一个 interface 可以同时继承多个 interface ,实现多个接口成员的合并。用逗号隔开要继承的接口。 interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
需要注意的是,尽管支持继承多个接口,但是如果继承的接口中,定义的同名属性的类型不同的话,是不能编译通过的。如下代码: interface Shape {
color: string;
test: number;
}
interface PenStroke extends Shape{
penWidth: number;
test: string;
}
另外关于继承还有一点,如果现在有一个类实现了 Square 接口,那么不仅仅需要实现 Square 的方法,也需要实现 Square 继承自的接口中的方法,实现接口使用 可索引类型接口interface和type的区别type 可以而 interface 不行
// 基本类型别名 type Name = string // 联合类型 interface Dog { wong(); } interface Cat { miao(); } type Pet = Dog | Cat // 具体定义数组每个位置的类型 type PetList = [Dog, Pet]
// 当你想获取一个变量的类型时,使用 typeof let div = document.createElement('div'); type B = typeof div
type StringOrNumber = string | number; type Text = string | { text: string }; type NameLookup = Dictionary<string, Person>; type Callback<T> = (data: T) => void; type Pair<T> = [T, T]; type Coordinates = Pair<number>; type Tree<T> = T | { left: Tree<T>, right: Tree<T> }; interface 可以而 type 不行interface 能够声明合并 interface User { name: string age: number } interface User { sex: string } /* User 接口为 { name: string age: number sex: string } */
另外关于type的更多内容,可以查看文档:TypeScript官方文档 接口的应用场景总结在项目中究竟怎么用,开篇已经举了两个例子,在这里再简单写一点,最近尝试了一下egg+ts,学习下。在写查询参数检验的时候,或者返回固定数据的时候,都会用到接口,看一段简单代码,已经看完了上面的文章,自己体会下吧。 import User from '../model/user'; import Good from '../model/good'; // 定义基本查询类型 // -- 查询列表时候使用的接口 interface Query { page: number; rows: number; disabledPage?: boolean; // 是否禁用分页,true将会忽略`page`和`rows`参数 } // 定义基本返回类型 type GoodResult<Entity> = { list: Entity[]; total: number; [propName: string]: any; }; // - 商品 export interface GoodsQuery extends Query { isOnline?: string | number; // 是否出售中的商品 goodsNo?: string; // 商品编号 goodsName?: string; // 商品名称 } export type GoodResult = QueryResult<Good>;
喜欢这篇文章?欢迎打赏~~
|
请发表评论