网站首页 > 技术文章 正文
TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6+ 的支持。TypeScript 的核心原则之一是对值所具有的结构进行类型检查。我们使用接口(Interfaces)来定义对象的类型。在定义函数或类时,遇到类型不明确的,可以使用泛型,泛型就是一个不确定的类型,调用时传入具体类型。本文与大家谈谈对于接口和泛型的理解。
一、接口
1. 初识接口
在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。
接口: 是对象的状态(属性)和行为(方法)的抽象(描述)。
需求: 创建人的对象, 需要对人的属性进行一定的约束:
/*
接口类型的对象
多了或者少了属性是不允许的
可选属性: ?
只读属性: readonly
*/
/*
需求: 创建人的对象, 需要对人的属性进行一定的约束
id是number类型, 必须有, 只读的
name是string类型, 必须有
age是number类型, 必须有
sex是string类型, 可以没有
*/
// 定义人的接口
interface IPerson {
readonly id: number;
name: string;
age: number;
sex?: string;
}
const person1: IPerson = {
id: 1,
name: "tom",
age: 20,
sex: "男",
};
2. 可选属性
接口里的属性不全都是必需的。有些是只在某些条件下存在,或者根本不存在。
// 定义人的接口
interface IPerson {
id: number;
name: string;
age: number;
sex?: string;
}
const person1: IPerson = {
id: 1,
name: "tom",
age: 20,
// sex: '男' // 可以没有
};
带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个 ? 符号。
可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误。
3. 只读属性
一些对象属性只能在对象刚刚创建的时候修改其值。你可以在属性名前用 readonly 来指定只读属性:
// 定义人的接口
interface IPerson {
readonly id: number;
name: string;
age: number;
sex?: string;
}
const person2: IPerson = {
id: 2,
name: "tom",
age: 20,
// sex: '男' // 可以没有
// xxx: 12 // error 没有在接口中定义, 不能有
};
person2.id = 2; // error 无法为“id”赋值,因为它是只读属性。
4. 任意属性
定义了任意属性后,对象变量中的属性个数才可以出现比接口的属性数量多的情况。
// 定义人的接口
interface IPerson {
readonly id: number;
name: string;
age: number;
sex?: string;
[propName: string]: any;
}
const person2: IPerson = {
id: 2,
name: "tom",
age: 20,
test: "111",
test1: 12,
};
5. 函数类型
接口能够描述 JavaScript 中对象拥有的各种各样的外形。除了描述带有属性的普通对象外,接口也可以描述函数类型。
为了使用接口表示函数类型,我们需要给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
/*
接口可以描述函数类型(参数的类型与返回的类型)
*/
interface SearchFunc {
(source: string, subString: string): boolean;
}
/*
这样定义后,我们可以像使用其它接口一样使用这个函数类型的接口。
下例展示了如何创建一个函数类型的变量,并将一个同类型的函数赋值给这个变量。
*/
const mySearch: SearchFunc = function (source: string, sub: string): boolean {
return source.search(sub) > -1;
};
console.log(mySearch("abcd", "bc"));
接口能够描述 JavaScript 中对象拥有的各种各样的外形。除了描述带有属性的普通对象外,接口也可以描述函数类型。
6. 接口定义多次产生的效果
在TS中,接口是可以多次声明的(声明同样名字的接口)。TS编译器会将名字相同的多个声明合并为一个声明。合并后的声明同时拥有多个声明的特性。
// 定义人的接口
interface IPerson {
readonly id: number;
age: number;
}
interface IPerson {
name: string;
sex: string;
}
const person2: IPerson = {
//报错
id: 2,
name: "tom",
age: 20,
};
会有报错信息:Property 'sex' is missing in type '{ id: number; name: string; age: number; }' but required in type 'IPerson'.
7. 类类型
类实现接口,与 C# 或 Java 里接口的基本作用一样,TypeScript 也能够用它来明确的强制一个类去符合某种契约。
/*
类类型: 实现接口
1. 一个类可以实现多个接口
2. 一个接口可以继承多个接口
*/
interface Alarm {
alert(): any;
}
interface Light {
lightOn(): void;
lightOff(): void;
}
class Car implements Alarm {
alert() {
console.log("Car alert");
}
}
8. 一个类可以实现多个接口
class Car2 implements Alarm, Light {
alert() {
console.log("Car alert");
}
lightOn() {
console.log("Car light on");
}
lightOff() {
console.log("Car light off");
}
}
9. 接口继承接口
和类一样,接口也可以相互继承。这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。
interface LightableAlarm extends Alarm, Light {
}
二、泛型
1. 初识泛型
指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。
下面创建一个函数, 实现功能: 根据指定的数量 count 和数据 value , 创建一个包含 count 个 value 的数组 不用泛型的话,这个函数可能是下面这样:
function createArray(value: any, count: number): any[] {
const arr: any[] = [];
for (let index = 0; index < count; index++) {
arr.push(value);
}
return arr;
}
const arr1 = createArray(11, 3);
const arr2 = createArray("aa", 3);
console.log(arr1[0].toFixed(), arr2[0].split(""));
2. 使用函数泛型
function createArray2<T>(value: T, count: number) {
const arr: Array<T> = [];
for (let index = 0; index < count; index++) {
arr.push(value);
}
return arr;
}
const arr3 = createArray2<number>(11, 3);
console.log(arr3[0].toFixed());
// console.log(arr3[0].split('')) // error
const arr4 = createArray2<string>("aa", 3);
console.log(arr4[0].split(""));
// console.log(arr4[0].toFixed()) // error
3. 多个泛型参数的函数
一个函数可以定义多个泛型参数
function swap<K, V>(a: K, b: V): [K, V] {
return [a, b];
}
const result = swap<string, number>("abc", 123);
console.log(result[0].length, result[1].toFixed());
4. 泛型接口
在定义接口时, 为接口中的属性或方法定义泛型类型。
在使用接口时, 再指定具体的泛型类型。
- 声明泛型类型: 在接口名的右侧 <T> // T K V
- 使用泛型: 接口体中
- 指定泛型的具体类型: 定义实现时, 接口名的右侧 <具体类型>
// 定义一个类,这个类是专门对数据进行增删改查的工具类
// 可以实例化一个对象用来保管数据
// 保管数据的话 需要一个接口限定
class User {
id: number;
name: string; //姓名
age: number; //年龄
constructor(name, age, id) {
this.id = id;
this.name = name;
this.age = age;
}
}
interface IbaseCRUD<T> {
data: T[];
add: (t: T) => void;
getById: (id: number) => T;
}
// 管理工具类
// 实例化这个类我就可以拿到一个工具对象用来管理东西
class UserCRUD implements IbaseCRUD<User> {
data: User[] = [];
add(u: User): void {
this.data.push(u);
}
getById(id: number) {
return this.data.find((item: User) => item.id === id);
}
}
let a1 = new UserCRUD();
let u1 = new User("zhaoliying", 35, 1);
let u2 = new User("yangmi", 36, 2);
a1.add(u1);
a1.add(u2);
console.log(a1.getById(1));
5. 泛型类
正文部分文本字体大小为15px可以插入代码 段后距24正文部分文本字体大小为15px可以插入代码 段后距24
1)声明泛型类型: 在类名的右侧 <T> // T K V
2)使用泛型: 类体中
3)指定泛型的具体类型: 创建类的实例时, 类名的右侧 <具体类型>
class GenericData<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
const genericNumber = new GenericData<number>();
genericNumber.zeroValue = 4;
genericNumber.add = function (x, y) {
return x + y;
};
console.log(genericNumber.add(genericNumber.zeroValue, 5));
let genericString = new GenericData<string>();
genericString.zeroValue = "abc";
genericString.add = function (x, y) {
return x + y;
};
console.log(genericString.add(genericString.zeroValue, "test"));
6. 泛型约束
如果我们直接对一个泛型参数取 length 属性, 会报错, 因为这个泛型根本就不知道它有这个属性。
// 没有泛型约束
function fn<T>(x: T): void {
// console.log(x.length) // error
}
我们可以使用泛型约束来实现:
interface Lengthwise {
length: number;
}
// 指定泛型约束
function fn2<T extends Lengthwise>(x: T): void {
console.log(x.length);
}
我们需要传入符合约束类型的值,必须包含必须 length 属性:
fn2("abc");
// fn2(123) // error number没有length属性
7. 泛型工具
?条件判断
条件判断会以一个条件表达式进行类型关系检测,从而在两种类型中选择其一。
T extends U ? X : Y
上述代码含义为:如果 T 包含的类型是 U 包含的类型的 '子集',那么取结果 X,否则取结果 Y。
实现一个简单的示例代码:
type WhatType<T> = T extends null | undefined ? never : T
let typeString: WhatType<string> = 'abc' // string 类型
let typeNull: WhatType<null> // never 类型
typeof
typeof 操作符用来在类型上下文中获取变量或者属性的类型。示例代码如下:
interface IPerson {
name: string;
age: number;
}
const user: IPerson = {
name: "jenny",
age: 18,
};
type student = typeof user; // IPerson
keyof
keyof 操作符用来获取某种类型的所有 key 值,返回一个联合类型。示例代码如下:
interface IPerson {
name: string;
age: number;
}
type allKey1 = keyof IPerson; // 'name' | 'age'
type allKey2 = keyof IPerson[]; // 'length | 'toString | 'pop' | 'push' | 'concat' | 'join' | ......
type allKey3 = keyof { [x: string]: IPerson }; // string | number
in
in 操作符用来遍历枚举类型。示例代码如下:
type keys = "a" | "b" | "c";
type obj = {
[p in keys]: any;
}; // { a: any, b: any, c: any}
infer
在条件类型语句中,可以用 infer 声明一个类型变量,并且对它进行使用。
infer 可以在 extends 的条件语句中推断待推断的类型。示例代码如下:
type ParamType<T> = T extends (...args: infer P) => any ? P : T;
interface User {
name: string;
age: number;
}
type Func = (user: User) => void;
type Param = ParamType<Func>; // Param = User
type AA = ParamType<string>; // string
Required
Required<T> 的作用就是将某个类型中的属性全部变为必选。
type Required<T> = {
[P in keyof T]-?: T[P];
};
-? 的作用就是把可选属性的可选性去掉,使该属性变成必选项。示例代码如下:
interface IPerson {
name?: string;
age?: number;
}
type person = Required<IPerson>;
// 相当于
// type person = {
// name: string;
// age: number;
// }
Readonly
Readonly<T> 的作用是将某个类型所有属性变为只读属性,也就意味着这些属性不能被重新赋值。
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
在每一个 key 前面加上 readonly。示例代码如下:
interface IPerson {
name: string;
age: number;
}
type person = Readonly<IPerson>;
// 相当于
// type person = {
// readonly name: string;
// readonly age: number;
// }
const obj: person = {
name: "lucy",
age: 18,
};
// 此处 TS 会报错:无法为 name 重新赋值,因为它是只读属性
obj.name = "jenny";
三、TypeScript中常用内置工具类型
TS中常用的工具类型,让写TS时效率大大提升,避免无意义的重复性定义。
1. Omit 省略/剔除
可以剔除已定义对象中,自己不需要的一部分形成新的定义类型。
interface UserObj {
readonly name: string; // readonly 只读属性 只能初始化定义 不能二次赋值
age: number;
id: number;
sex: 0 | 1;
address: string;
weight: number;
}
// 剔除省略自己不需要的
type Person = Omit<UserObj, "number" | "sex" | "address" | "weight">;
// 此时Person 等同于 Person1
interface Person1 {
readonly name: string;
id: number;
}
2. Pick 采集
可以采集已定义对象中,自己需要的一部分形成新的定义类型。
interface UserObj {
readonly name: string;
age: number;
id: number;
sex: 0 | 1;
address: string;
weight: number;
}
// 采集需要的
type Person = Pick<UserObj, "name" | "id">;
// 此时Person 等同于 Person1
interface Person1 {
readonly name: string;
id: number;
}
总结
在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型,接口: 是对象的状态(属性)和行为(方法)的抽象(描述)。接口内部不能写属性的值和方法的实现,只是声明一下。接口是用来限制对象数据,是一个规范。泛型指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。
猜你喜欢
- 2024-10-12 php 给app 或前端封装api 接口——json格式
- 2024-10-12 前端基础:vue中Axios的封装和API接口的管理
- 2024-10-12 Vue项目中实现用户登录及token验证
- 2024-10-12 Java实战系列-前端VUE代码开发及接口流程设计(1)
- 2024-10-12 你还不了解typescript中接口和类型别名的用法区别?本文帮你总结
- 2024-10-12 前端开发:JavaScript API应用程序编程接口
- 2024-10-12 Java 中间的接口还可以这样用,你知道吗?
- 2024-10-12 Web前端接口画中画(Picture-in-Picture)介绍
- 2024-10-12 vue 项目接口管理 vue3接口
- 2024-10-12 移动端前端车牌识别接口 车牌识别java
你 发表评论:
欢迎- 最近发表
-
- 前端流行框架Vue3教程:13. 组件传递数据_Props
- 前端必看!10 个 Vue3 救命技巧,解决你 90% 的开发难题?
- JAVA和JavaScript到底是什么关系?是亲戚吗?
- Java和js有什么区别?(java和javascript的区别和联系)
- 东方标准|Web和Java的区别,如何选择这两个专业
- 前端面试题-JS 中如何实现大对象深度对比
- 360前端一面~面试题解析(360前端笔试)
- 加班秃头别慌!1 道 Vue 面试题,快速解锁大厂 offer 通关密码
- 焦虑深夜刷题!5 道高频 React 面试题,吃透 offer 稳了
- 2025Web前端面试题大全(整理版)面试题附答案详解,最全面详细
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端md5加密 (49)
- 前端路由 (55)
- 前端数组 (65)
- 前端定时器 (47)
- 前端懒加载 (45)
- 前端接口 (46)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle查询数据库 (45)
- oracle约束 (46)
- oracle 中文 (51)
- oracle链接 (47)
- oracle的函数 (57)
- mac oracle (47)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)