范闲?不不不,是泛型浅聊一下 最近《庆余年》也是非常的爆火啊,今天时间管理大师在打开电脑学习的那一瞬间竟然把泛型看成了范
浅聊一下
最近《庆余年》也是非常的爆火啊,今天时间管理大师在打开电脑学习的那一瞬间竟然把泛型看成了范闲,别搞别搞,今天将继续TypeScript中泛型的学习...
在金三银四的尾巴上了车,如果你也和我一样也想在大三找一份还不错的实习,欢迎掘友们私聊我交流面经(wechat: LongLBond)
泛型
先来想象一个场景,我定义了一个returnX
的方法,我想要传入的参数可以是多种类型,不管是传入number
也好还是string
也好,并且返回值也为对应的类型,这该如何是好?那就不得不提到我们的泛型了...
function returnX<T>(para: T): T{
return para
}
通过< >
接受一个T
类型,然后限制传入的para
和返回值的类型为T
类型
let item1 = returnX<string>("123");
let item2 = returnX<number>(123);
多个类型参数
定义泛型的时候,我们可以一次性定义多个类型参数,例如我们可以同时定义 T 和 U 两个泛型:
function returnX2<T,U>(para:[T,U]):U{
return para[1]
}
泛型变量
泛型变量 T 当作类型的一部分使用
function returnX3<T>(para: Array<T>){
for(let i in para){
console.log(para[i])
}
}
returnX3<number>([1,2,3])
泛型接口
泛型也可以用于接口声明
interface IReturnX<T>{
x: T,
say:(words:T) => T
}
let j: IReturnX<number> = {
x: 123,
say: function(words: number){
return words + this.x;
}
}
泛型类
泛型除了可以在函数中使用,还可以在类中使用,它既可以作用于类本身,也可以作用于类的成员函数。
class St<T> {
private arr: T[] = [];
public push(item: T) {
this.arr.push(item);
}
public getArr(){
return this.arr;
}
public pop() {
this.arr.pop();
}
}
const s = new St<number>()
s.push(1)
s.push(2)
s.push(3)
console.log( s.getArr() );
泛型约束
如果我们想约束这个<T>
为几种类型之一,那么可以使用<T extends XX >
的方式来实现
type Params = number | string | boolean;
class St<T extends Params> {
private arr: T[] = [];
public push(item: T) {
this.arr.push(item);
}
public getArr(){
return this.arr;
}
public pop() {
this.arr.pop();
}
}
const s = new St<number>()
如果我们给了Params
之外的类型:
使用多重类型进行泛型约束
在上面,我们使用Params
来约束T
的类型,T
可以选中Parmas
中的一种类型,但是如果我想选中多种类型这该如何是好?
interface Fir {
doSomething():number;
}
interface Sec {
doSomethingElse():string;
}
class St2<T extends Fir,Sec>{
private getPro: T;
constructor(getPro:T){
this.getPro = getPro;
}
useT() {
this.getPro.doSomething();
this.getPro.doSomethingElse();
}
}
有些掘友可能会这么做,但是这样真的将 Fir和Sec都选中了吗?
结果显而易见,这里只选中了Fir...那我分别选中不就好啦?
class Demo<T extends Fir, T extends Sec> { // 标识符“T”重复
...
}
很遗憾,语法错误...别卖关子了,咱到底该怎么做?
interface Child extends Fir,Sec{
...
}
秒了,直接创建一个子接口,然后通过子接口来进行泛型约束
class St2<T extends Child>{
private getPro: T;
constructor(getPro:T){
this.getPro = getPro;
}
useT() {
this.getPro.doSomething();
this.getPro.doSomethingElse();
}
}
到这就完了吗?别急,我们还能用交叉类型来进行多类型约束
class St2<T extends Fir&Sec>{
private getPro: T;
constructor(getPro:T){
this.getPro = getPro;
}
useT() {
this.getPro.doSomething();
this.getPro.doSomethingElse();
}
}
泛型约束与索引类型
想象一个场景,有一个函数接受两个参数,第一个参数为一个对象,第二个参数为第一个参数上的一个属性,函数返回这个对象属性对应的值
function fn(obj:object,key :string) {
return obj[key];
}
我们来看看它有什么错误
原来是因为TypeScript编译器无法确认obj上是否存在key属性,所以会认为这段代码是不安全的,那如果我就是要返回obj[key]
呢?
function fn<T extends object,U extends keyof T>(obj:T,key :U) {
return obj[key];
}
T extends object
:这里,T
是一个泛型类型参数,它表示任何继承自(或说“是”)object
的类型。extends
关键字用于约束泛型类型,意味着T
可以是任何对象类型,包括具有任意属性和方法的自定义类型。这个约束确保了T
至少是一个对象,因此我们可以对它进行索引操作。U extends keyof T
:U
是第二个泛型类型参数,它的约束是keyof T
。keyof
是 TypeScript 中的一个操作符,用于获取一个对象类型的所有键(属性名)组成的联合类型。因此,U
必须是T
类型对象的某个键。这个约束非常强大,因为它确保了传递给函数的key
参数实际上是obj
对象上的一个属性名。
来看使用:
class St<T> {
public name:string = 'oldSix'
}
function fn<T extends object,U extends keyof T>(obj:T,key :U) {
return obj[key];
}
const s = new St<number>()
console.log(fn(s,'name'));
泛型与new
来看以下场景
function fac<T>(type:T):T{
return new type();
}
我们想要返回一个type类型的对象,但是想法很丰满,现实很骨感,不出所料,报错了
为什么?因为我们不知道这个type是不是一个构造函数,假设我传入一个数字1,那 new 1()
必然不是合理的...
function fac<T>(type: { new(): T }): T {
return new type();
}
使用new
就可以让代码合理,参数 type
的类型 {new(): T}
就表示此泛型 T
是可被构造的,在被实例化后的类型是泛型 T
结尾
罪过罪过,又是摸鱼学习的一天~~
转载自:https://juejin.cn/post/7372078061896400933