Let's take a look on useCallback
types:
/**
* `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;
As you might have noticed, callback
is a subtype of (...args: any[]) => any
. It means that any
is explicitly provided for args
. This is why noImplicitAny
does not work in this case, because any
is explicit.
As far as I understood and you have noticed - react team wants to replace current callback
type with (...args: never[]) => unknown
.
This subtype provides never
type for callback argument.
useCallback((arg) => arg.toString(), []); // error because [arg] is never
arg = 0
does not work in a case of never
because never
is a bottom type. You are not allowed to assign any/any
type to never
.
Consider this example:
let x: never;
let y: any;
x = y; // error
never
is assignable to everything but not vice versa. Please see the docs
The never type is a subtype of, and assignable to, every type; however, no type is a subtype of, or assignable to, never (except never itself). Even any isn’t assignable to never.
By in most cases when the argument's type isn't specified
I assume you want to forbid using any
. I'm not sure whether react team can provide such restriction, because many people are using any
during the migration from js
to ts
.
If you want to use default parameter, type of argument should be either any
or has the same type as default parameter. Hence, we have a collision. You want to disallow any
and at the same type you want to use default parameter.
You are able to override useCallback
type in this way:
// 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" */) => {}, []);
};
Playground
Instead CustomError
you can use any type you want. However, using <T extends (...args: never[]) => unknown>
might be a better solution than mine.