首先是先看文档,从文档中可以看到:
现在在有条件类型的
extends子语句中,允许出现infer声明,它会引入一个待推断的类型变量。 这个推断的类型变量可以在有条件类型的true分支中被引用。 允许出现多个同类型变量的infer。例如,下面代码会提取函数类型的返回值类型:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
从这段话中可以看到,infer的作用是占位置,代替一个会被infer(推导)出来的类型变量,而他必须写在extends的true分支中。
所以很明显他的作用就是从类型中获取类型,例如函数的入参,出参,或者对象的某个key,某个value的类型。
下面举个例子:
// 假设有一个函数,是从某个包中导出的
type NoExport = string;
export const func = (param: NoExport) => {
return param;
};
假设你再次包装了这个func
此时入参你当然应该声明为 NoExport,但是你此时会报错,因为没有全局定义过NoExport或者导出给你使用
// error: Cannot find name 'NoExport'.ts(2304)
const customFunc = (param: NoExport) => {
// doing something but type never change
return func(param);
};
那么如何正常声明这个param呢? 当然是使用我们的infer,因为我们就是要从 类型中获取类型
// 需要定义这个类型,从而获取函数的第一个参数的类型
type ReturnFirstType<T> = T extends (first: infer U) => unknown ? U : never;
//由于连func的函数类型也没有显式的到处给我们使用,所以我们使用typeof func获取函数类型
const customFunc = (param: ReturnFirstType<typeof func>) => {
// doing something but type never change
return func(param);
};
什么?这个例子是虚假的,来个真实的例子,OK。
真实案例:封装一个防抖useEffect
const useDebounceEffect = (fn: any, dependencies?: any) => {
const debounceFn = useCallback(debounce(fn), []);
useEffect(debounceFn, dependencies);
};
具体实现就不讲了,专注类型声明
我们可以看到两个入参的类型,我们根本就不关注,因为它的类型安全由useEffect保证,所以我们只需要说一句,拿来吧你,useEffect
type ReturnFirstType<T> = T extends (first: infer U) => unknown ? U : never;
type ReturnSecondType<T> = T extends (first: unknown, second: infer U) => unknown ? U : never;
const useDebounceEffect = (
fn: ReturnFirstType<typeof useEffect>,
dependencies?: ReturnSecondType<typeof useEffect>
) => {
const constFn = useCallback(debounce(fn), []);
useEffect(constFn, dependencies);
};