为useCallback包装的函数启用noImplicitAny ?



React的useCallback是一个函数的包装器,它返回与输入相同的类型,这里是TS类型:

function useCallback<T extends (...args: any[]) => any>(
callback: T,
deps: DependencyList,
): T;

如果我没有为输入函数指定参数类型,我希望noImplicitAny会导致错误。但是,这不会导致错误:

const fn = useCallback(arg => {}, []);

可以将useCallback的类型更改为this,以在大多数情况下,当参数的类型未指定时触发错误:

function useCallback<T extends (...args: never[]) => unknown>(
callback: T,
deps: DependencyList,
): T;

const fn = useCallback(arg => arg.toString()); // Property 'toString' does not exist on type 'never'.
const fn = useCallback((arg: number) => arg.toString()); // No errors

然而,这似乎在某些情况下有问题。例如,默认参数:

const fn = useCallback((arg = 0) => arg.toString()); // Type 'number' is not assignable to type 'never'.

通常,TS推断arg的类型。如果我手动指定arg的类型,那么Eslint的@typescript-eslint/no-inferrable-types规则会产生一个错误。

是否有更好的useCallback类型来解决这些问题?

让我们来看看useCallback类型:

/**
* `useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
* has changed.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usecallback
*/
// TODO (TypeScript 3.0): <T extends (...args: never[]) => unknown>
function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T;

您可能已经注意到,callback(...args: any[]) => any的子类型。这意味着any是为args显式提供的。这就是为什么noImplicitAny在这种情况下不起作用,因为any是显式的。据我所知,你已经注意到- react团队想用(...args: never[]) => unknown取代当前的callback类型。

此子类型为回调参数提供never类型。

useCallback((arg) => arg.toString(), []); // error because [arg] is never

arg = 0never的情况下不起作用,因为never是底部类型。不允许为never分配任何/any类型。考虑这个例子:

let x: never;
let y: any;
x = y; // error

never可赋值给所有对象,反之则不然。请参阅文档

never类型是所有类型的子类型,且可赋值给所有类型;然而,没有类型是never的子类型或可赋值的类型(除了never本身)。即使any也不能赋值给never。

Byin most cases when the argument's type isn't specified我假设你想禁止使用any。我不确定react团队是否可以提供这样的限制,因为在从js迁移到ts的过程中,很多人都在使用any

如果你想使用默认参数,参数的类型应该是any或者与默认参数的类型相同。因此,我们有一个碰撞。您想要禁用any,并且在相同的类型下您想使用默认参数。

您可以通过以下方式重写useCallback类型:

// credits goes to https://stackoverflow.com/questions/55541275/typescript-check-for-the-any-type
type IfAny<T, Y, N> = 0 extends 1 & T ? Y : N;
type IsAny<T> = IfAny<T, true, false>;
type ValidateArguments<T extends any[]> = IsAny<
T[number]
> extends true
? 'Custom Error'[] // please provide any type you want
: T;
type HandleCallback<T extends (...args: any[]) => any> = (
...args: ValidateArguments<Parameters<T>>
) => ReturnType<T>;
declare module "react" {
function useCallback<T extends (...args: any[]) => any>(
callback: HandleCallback<T>,
deps: any[]
): T;
}
const App = () => {
const result = useCallback((arg /** "Custom Error" */) => {}, []);
};

游乐场

代替CustomError,你可以使用任何你想要的类型。然而,使用<T extends (...args: never[]) => unknown>可能是一个比我更好的解决方案。

相关内容

  • 没有找到相关文章

最新更新