泛型:组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,用户就可以以自己的数据类型来使用组件。更加灵活
泛型接口
//泛型接口的第一种定义方式 当这个接口去限制函数,推荐使用这个 interface IPrint { <T>(value: T): T } const print: IPrint = value => value print<string>('abc') //在函数调用时候显示传入T类型 print('bbb') //这是简写 这样更普遍 利用了类型推断 编译器自动根据所传参数类型'abc'确定T的类型 // 泛型接口的第二种定义方式 当这个接口去实现类implement 推荐使用这种定义方式。 interface IIdentity1<T> { (arg: T): T } //这样 我就必须在函数定义的时候传入T类型,但是我不知道在函数定义时候传入什么类型。 而不是调用时候传入T类型,这种不好 const print1: IIdentity1<any> = value => value //这里IIdentity1必须要求我传入T的类型。传入具体的类型不太好。因为到时候限制我的调用,只能传入具体的类型,所以不得已传入any,传入any后面调用是没有提示的 const r=print1(100) //r在这个时候是不明确它的类型的,编辑器是没有任何提示 const print2: IIdentity1<string> = value => value const r2=print2('abc') //r2明确知道是string类型。但是也只能传入string类型数据,失去了泛型的意义 // print2(100)Error Argument of type 'number' is not assignable to parameter of type 'string'.
例子2
// 定义泛型接口 interface IAddFunc { <T>(value1: T, value2: T): string | number } const add: IAddFunc = (value1, value2) => { // 直接 return value1+value2 是会报错的 Operator '+' cannot be applied to types 'T' and 'T'. if (typeof value1 === 'string' && typeof value2 === 'string') return value1 + value2 if (typeof value1 === 'number' && typeof value2 === 'number') return value1 + value2 } console.log(add('aaa', 'bbb')) console.log(add(10, 20))
更简洁的写法
interface IAddFunc {
<T>(value1: T, value2: T): T
}
const add: IAddFunc = <IAddFunc>(value1, value2) => { //<IAddFunc>是类型断言 尝试用as但是报错了why
return value1 + value2
}
console.log(add(1, 2))
console.log(add('a', 'b'))
// add(1, 'a') //Error
泛型类
class ClassName<T> //T可以是基本数据类型:number|string; 也可以是自定义类类型。其他类型应该也可以传进来当作T的类型
// 泛型类 class MinClass<T>{ list: Array<T> = [] push(value: T): void { this.list.push(value) } getMin(): T { let minIndex = 0 for (let i: number = 1; i < this.list.length; i++) { if (this.list[minIndex] > this.list[i]) minIndex = i //找出最小值索引 } return this.list[minIndex] } } // 数字类型 const minNumber = new MinClass<number>() minNumber.push(1) minNumber.push(30) minNumber.push(5) minNumber.push(-7) minNumber.push(0) minNumber.push(100) const minNum = minNumber.getMin() //minNum可以确定是number类型 无需显示表示,如果要表示就是number类型
//字符串类型 const minString = new MinClass<string>() minString.push('f') minString.push('d') minString.push('4') minString.push('ebe') minString.push('few') minString.push('wewr') const minStr = minString.getMin() ///minStr可以确定是string类型 无需显示表示,如果要表示就是string
class User { name: string; age: number; constructor(name: string, age: number) { this.name = name this.age = age } } class Article { title: string; time: Date desc?: string; constructor(title: string, desc?: string, time: Date = new Date) { this.title = title this.time = time this.desc = desc } } class SqlDb<T>{ //T可以是自定义类类型 我想甚至接口都可以传进来 list: Array<T> = [] //定义一个空泛型对象 add(item: T): void { this.list.push(item) } } const u1 = new User('zs', 10) const u2 = new User('lisi', 12) const article = new Article('三国演义', '曹操-孙权-刘备') const article1 = new Article('红楼梦', '贾宝玉-林黛玉-薛宝钗') const userDB = new SqlDb<User>() //看这里 传入自定义类型User userDB.add(u1) userDB.add(u2) const articleDB = new SqlDb<Article>() //看这里 传入自定义类型Article articleDB.add(article) articleDB.add(article1)
泛型约束
例子1
interface ILength { length: number //限制泛型必须有length 属性 } interface ILoggingIdentity { <T extends ILength>(value: T): T // <T extends ILength> 表示T类型必须有length属性 } const loggingIdentity: ILoggingIdentity = value => { console.log(value.length);//我输出length属性 肯定是希望我传入的变量是有length属性的 return value } loggingIdentity('abc') loggingIdentity({ name: 'zs', age: 10, length: 10 })
泛型类型别名 type定义
// 接口和type interface定义一个实实在在的接口,接口是一个真正的类型。type一般是定义别名,大白话:type 就是给一个类型起一个新名字。不会产生一个新的类型 //类型别名type不能用extends implements扩展接口, interface可以 // 泛型类型别名 type定义 type CartType<T> = { list: Array<T> } | Array<T> //| 或者意思 const cart1:CartType<string>={list:['1','2']} const cart2:CartType<number>=[1,2,3]
综合案例
里面有自定义类接口约束
/* 定义一个操作数据库的库同时支持mysql mssql mongDB(泛化),同时都有add update delete get方法(接口实现类) //注意描述:约束统一的规范(接口);代码的重用(泛型) */ interface DBI<T> { //接口去定义方法 然后用类去实现这个接口implement add: (item: T) => boolean update: (item: T, id: number) => boolean deleteById: (id: number) => boolean getById: (id: number) => T } interface IId { //接口约束T必须有Id属性 id: number } // 定义一个操作mysql数据库的类 注意:要实现泛型接口,这个类也应该是一个泛型类 // 新的知识点 自定义类约束 <T extends IId> 约束T自定义类实例有id属性 class MySqlDb<T extends IId> implements DBI<T>{ list: Array<T> = [] add(item: T) { this.list.push(item) return true } update(item: T, id: number) { return true } deleteById(id: number) { this.list = this.list.filter(item => item.id !== id) return true } getById(id: number): T { return this.list.find(item => item.id === id) } } class User { id: number user: string; password: string constructor(id: number, user: string, password: string) { this.user = user this.password = password this.id = id } } const u1 = new User(1000, 'zs', '123456') const u2 = new User(1001, 'lisi', '123456') const mySqlDb = new MySqlDb<User>() mySqlDb.add(u1) mySqlDb.add(u2) console.log('8888888888888888'); console.log(mySqlDb.getById(1001)) mySqlDb.deleteById(1001) console.log(mySqlDb.list); //定义一个操作mssql数据库的类 class MsSqlDb<T> implements DBI<T>{ add: (item: T) => boolean; update: (item: T, id: number) => boolean; deleteById: (id: number) => boolean; getById: (id: number) => T; } //定义一个操作mongDB数据库的类 class MonDb<T> implements DBI<T>{ add: (item: T) => boolean; update: (item: T, id: number) => boolean; deleteById: (id: number) => boolean; getById: (id: number) => T; }