-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
[no-explicit-any] Option to disable rule for generic constraints #642
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Does |
- type IsString<T extends any> = T extends string ? true : false;
+ type IsString<T> = T extends string ? true : false; Nitpicking, but - type CodomainOf<T extends Record<string, any>> = T extends Record<string, infer R> ? R : never;
+ type CodomainOf<T extends Record<string, unknown>> = T extends Record<string, infer R> ? R : never; @j-f1 is correct, you can use |
I'm going to close and reject this for the following reasons:
|
@bradzacher While I usually agree, |
@milesjs In that case, you replace unknown with
|
tsc already knows and keeps track of what is covariant and what is contravariant, so it may be better if this would be done in typescript. |
@anilanar |
could you share more about your setup? It's hard to provide advice from a screenshot... export default function milesjFunc<T extends Context = Context>(
titleOrWorkUnit: unknown,
action?: Action<T, Input, Output>,
scope?: unknown,
) |
Probably, but that's adding far more generics all over the place for this one use case. https://github.com/milesj/boost/blob/master/packages/pipeline/src/createWorkUnit.ts#L15 I'm usually anti-any, but in cases like this it would be nice. But I'm also not a fan adding eslint line disables anywhere. |
Investigating this more - you should be able to achieve this as follows: function createWorkUnit<Input, Output = Input, TCtx extends Context = Context>(
titleOrWorkUnit: string | WorkUnit<any, Input, Output>,
action?: Action<TCtx, Input, Output>,
scope?: unknown,
): WorkUnit<any, Input, Output>; In usage you will never have to instantiate the generic, as its type will be inferred for you automatically based on usage: createWorkUnit('foo'); // TCtx === Context
class FooContext extends Context {}
createWorkUnit( // TCtx === FooContext
'foo',
(context: FooContext, value: string, runner: any) => value,
); The problem with adding this option is that it opens a door very widely - there's no granularity. The point of the rule is to not use any, as there is almost always a better alternative. Very rarely should you actually have to reach for it. Some things will take a bit of work to do type-safely, but it is doable. The point of eslint disables is to clearly document that you are working around the eslint rule for a reason. Which is exactly the case here. IMO this is completely valid code, and I wouldn't complain if I saw this in a PR: function createWorkUnit<Input, Output = Input>(
titleOrWorkUnit: string | WorkUnit<any, Input, Output>,
// No point introducing a generic for this, as the type of the context doesn't matter to this function
// eslint-disable-next-line @typescript-eslint/no-explicit-any
action?: Action<any, Input, Output>,
scope?: unknown,
): WorkUnit<any, Input, Output>; |
I use constraints in many of my generics, so having to add additional generics to solve this ESLint rule is completely overkill. I'll just turn it off. Thanks for the feedback. |
I think my use case is a common one: constraining a generic type to be an array. Is there a way that avoids export function useDeps<T extends any[]>(deps: T): T {
const depsRef = useRef<T>();
if (!isEqualArray(depsRef.current, deps)) {
depsRef.current = deps;
}
return depsRef.current;
} |
Use |
You cannot because its contravariant. |
Seems to work fine though? declare function isEqualArray<T extends readonly unknown[]>(arg1: T, arg2: T): boolean;
declare function useRef<T>(): {current: T};
export function useDeps<T extends unknown[]>(deps: T): T {
const depsRef = useRef<T>();
if (!isEqualArray(depsRef.current, deps)) {
depsRef.current = deps;
}
return depsRef.current;
}
const x1 = useDeps([1,2,3]); // typeof x1 === number[]
const x2 = useDeps(['a', 'b', 'c'] as string[]); // typeof x2 === string[]
const x3 = useDeps([1,2,3] as const); // error: type 'readonly [1, 2, 3]' is 'readonly' and cannot be assigned to the mutable type 'unknown[]'
// for bonus points you can allow tuples by adding readonly
export function useDepsTuple<T extends readonly unknown[]>(deps: T): T {
const depsRef = useRef<T>();
if (!isEqualArray(depsRef.current, deps)) {
depsRef.current = deps;
}
return depsRef.current;
}
const x4 = useDepsTuple([1,2,3]); // typeof x4 === number[]
const x5 = useDepsTuple(['a', 'b', 'c'] as string[]); // typeof x5 === string[]
const x6 = useDepsTuple([1,2,3] as const); // typeof x6 === readonly [1,2,3] |
@bradzacher Thanks for taking the time to respond with that example. You're right. I had a fundamental misunderstanding of If anyone else stumbles on this issue, check out When to use never and unknown in TypeScript. TL;DR:
|
The way I explain |
@milesj - an interesting thing to note that I hadn't thought about (thanks @mmun) - you can actually use function createWorkUnit<Input, Output = Input>(
titleOrWorkUnit: string | WorkUnit<never, Input, Output>,
action?: Action<never, Input, Output>,
scope?: unknown,
): WorkUnit<never, Input, Output>; |
@bradzacher Oh wow, never would of though about that either. I'll give that shot. For a few of those scenarios, I've been using |
Repro
Expected Result
No errors for explicit
any
if they are used in generic constraints.Actual Result
There is no such option to disable errors/warnings for explicit
any
usage in generic constraints.The text was updated successfully, but these errors were encountered: