泛型
软件工程中,我们不仅要创建一致的定义良好的api,同时也要考虑可重用性。
组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能
泛型就是解决类、接口、方法的复用性,以及对不特定数据类型的支持。
要求:传入的参数和返回的参数一致
函数的泛型
function getDate<T>(value: T): T {
return value;
}
console.log(getDate<number>(123));
tips: 这里的T可改成其他任意值但定义的值,和传入的参数以及返回的参数是一样的,一般默认写法是T,也是业内规范的选择。
类的泛型
class MinClass<T> {
public list: T[] = [];
//添加
add(value: T): void {
this.list.push(value);
}
//求最小值
min(): T {
//假设这个值最小
let minNum = this.list[0];
for (let i = 0; i < this.list.length; i++) {
//比较并获取最小值
minNum = minNum < this.list[i] ? minNum : this.list[i];
}
return minNum;
}
}
//实例化类 并且指定了类的T的类型是number
let minClass = new MinClass<number>();
minClass.add(23);
minClass.add(5);
minClass.add(2);
console.log(minClass.min());
//实例化类 并且指定了类的T的类型是string,则其方法的传参和返回都是string类型
let minClass2 = new MinClass<string>();
minClass2.add("23");
minClass2.add("5");
minClass2.add("2");
console.log(minClass2.min());
接口的泛型
- 第一种写法
interface ConfigFn {
//规范参数类型,返回值类型
<T>(value: T): T;
}
let getData: ConfigFn = function <T>(value: T): T {
return value;
};
console.log(getData<string>("z11"));
- 第二种写法
interface ConfigFn<T> {
//参数类型 ,返回值类型
(value: T): T;
}
//接口方法
function getData<T>(value: T): T {
return value;
}
//使用接口
let myGetDate: ConfigFn<string> = getData;
console.log(myGetDate("3"));
tips:接口的泛型只针对函数类型的接口
类当做参数传入泛型类
//用户类--和数据库表字段进行映射
class User {
username: string | undefined;
password: string | undefined;
//构造函数-初始化参数
constructor(param: {
username: string | undefined;
password?: string | undefined;
}) {
this.username = param.username;
this.password = param.password;
}
}
//数据库类
class Db<T> {
add(user: T): boolean {
console.log(user);
return true;
}
updated(user: T, id: number): boolean {
console.log(user, id);
return true;
}
}
let u = new User({
username: "张三",
});
//u.username = "李四";
u.password = "111111";
let db = new Db<User>();
db.add(u);
db.updated(u, 1);
tips: 类的参数名和类型都做了约束。
模块
内部模块称为命名空间,外部模块简称为模块,模块在其自身的作用域里执行,而不是在全局作用域里;
这意味着定义在一个模块里的变量、函数、类等等在模块外部是不可见的,除非你明确的使用export形式之一导出它们。
相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导人它们,可以使用import形式之一。
我们可以一些公共的功能单独抽离成一个文件作为一个模块。
模块里面的变量、函数、类等默认是私有的,如果我们要在外部访问模块里面的数据(变量、函数、类)
我们需要通过export暴露模块里面的数据(变量、函数、类…)。
暴露后我们通过import引入模块就可以使用模块里面暴露的数据(变量、函数、类…)
//modules/db.ts
function getDate(): any[] {
console.log("获取数据");
return [
{
userName: "张三",
},
{
userName: "李四",
},
];
}
//一个模块里面可以用多次
// export { getDate };
//一个模块里面只能用一次
export default getDate;
import { getDate as getDbDate } from "./modules/db";
import getDbDate from "./modules/db";
getDbDate();
tips: 这个调试时浏览器中不能直接使用,可在node和weakpack的环境中调试。
命名空间
在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内
TypeScript的命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象。
命名空间和模块的区别
1.命名空间:内部模块,主要用于组织代码,避免命名冲突。
2.模块:ts外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间。
// modules/Animal.ts
export namespace A {
interface Animal {
name: String;
eat(): void;
}
export class Dog implements Animal {
name: String;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log("我是" + this.name);
}
}
}
export namespace B {
interface Animal {
name: String;
eat(): void;
}
export class Dog implements Animal {
name: String;
constructor(theName: string) {
this.name = theName;
}
eat() {}
}
}
import { A, B } from "./modules/Animal";
let ee = new A.Dog("小贝");
ee.eat();