TS学习小记

  • 泛型

泛型可以理解为宽泛的类型,通常用于类和函数。用通俗的话来说就是“把明确类型的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型,简单点来讲我们可以将泛型理解成为把类型当作参数一样去传递。

function identity<T>(arg: T): T { 
  return arg;
}

// 调用identity时传入name,函数会自动推导出泛型T为string,自然arg类型为T,返回值类型也为T
const userName = identity('name');
// 同理,当然你也可以显示声明泛型
const id = identity<number>(1);
  • 接口泛型位置

之所以将接口中的泛型单独拉出来和大家讲述,是因为在日常工作中经常会碰到一些同事对于泛型接口位置的不理解。

空口无凭,我们来看看这样一个简单的例子:

// 定义一个泛型接口 IPerson表示一个类,它返回的实例对象取决于使用接口时传入的泛型T
interface IPerson<T> {  
  // 因为我们还没有讲到unknown 所以暂时这里使用any 代替  
  new(...args: unknown[]): T;
}
 
function getInstance<T>(Clazz: IPerson<T>) { 
  return new Clazz();
}
 
// use it
class Person {}
 
// TS推断出函数返回值是person实例类型
const person = getInstance(Person);

这里上下两个例子特别像强调的是关于泛型接口中泛型的位置是代表完全不同的含义:

当泛型出现在接口中时,比如interface IPerson<T> 代表的是使用接口时需要传入泛型的类型,比如IPerson<T>。

当泛型出现在接口内部时,比如第二个例子中的 IPerson接口代表一个函数,接口本身并不具备任何泛型定义。而接口代表的函数则会接受一个泛型定义。换句话说接口本身不需要泛型,而在实现使用接口代表的函数类型时需要声明该函数接受一个泛型参数。

趁热打铁,我们来看这样一个例子:当我们希望实现一个数组的 forEach 方法时,尝试使用泛型来实现:

// 定义callback遍历方法 两种方式 应该采用哪一种?
type Callback = <T>(item: T) => void
// 第二种声明方式
type Callback<T> = (item: T) => void;
 
const forEach = <T>(arr: T[], callback: Callback) => { 
  for (let i = 0; i < arr.length - 1; i++) {  
    callback(arr[i]) 
  }
};
 
forEach(['1', 2, 3, '4'], (item) => {});
  • 泛型约束

所谓泛型约束,通俗点来讲就是约束泛型需要满足的格式。提到它,有一个非常经典的案例:

// 定义方法获取传入参数的length属性
function getLength<T>(arg: T) {  
  // throw error: arr上不存在length属性 
  return arg.length;
}

这里,我们定义了一个 getLength 方法,希望函数获取传入参数的 length 属性。

因为传入的参数是不固定的,有可能是 string 、 array 、 arguments 对象甚至一些我们自己定义的 { name:"19Qingfeng", length: 100 },所以我们为函数增加泛型来为函数增加更加灵活的类型定义。

可是随之而来的问题来了,那么此时我们在函数内部访问了 arg.length 属性。但是此时,arg 所代表的泛型可以是任意类型。

比如我们可以传入一个 boolean ,那么此时函数中的泛型 T 代表 boolean 类型,访问 boolean.length ? 这显然是一个 bug 。

那么如果解决这个问题呢,当然就提到了所谓的泛型约束 extends 关键字。

我们先来看看如何使用它:

interface IHasLength { 
  length: number;
}
 
// 利用 extends 关键字在声明泛型时约束泛型需要满足的条件
function getLength<T extends IHasLength>(arg: T) { 
  // throw error: arr上不存在length属性 
  return arg.length;
}
 
getLength([1, 2, 3]); // correct
getLength('123'); // correct
getLength({ name: '19Qingfeng', length: 100 }); // correct
// error 当传入true时,TS会进行自动类型推导 相当于 getLength<boolean>(true)
// 显然 boolean 类型上并不存在拥有 length 属性的约束,所以TS会提示语法错误getLength(true);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值