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.