Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Enhancement: [no-explicit-any] Option to disable rule for generic constraints (revisited) #7751

Closed as not planned
Labels
enhancement: plugin rule optionNew rule option for an existing eslint-plugin rulepackage: eslint-pluginIssues related to @typescript-eslint/eslint-pluginwontfixThis will not be worked on
@kasper573

Description

@kasper573

Before You File a Proposal Please Confirm You Have Done The Following...

My proposal is suitable for this project

  • I believe my proposal would be useful to the broader TypeScript community (meaning it is not a niche proposal).

Link to the rule's documentation

https://typescript-eslint.io/rules/no-explicit-any/

Description

I'd like to revisitthis issue for adding an option to theno-explicit-any rule that allows the use ofany as generic constraint. That issue was seemingly closed by a bot, so I'm not even sure if that issue reached a real consensus. Also, I've written a few code examples that I think add some value to the discussion, which is why I'm opening this issue even if there's technically another existing (closed) issue.

Disclaimer: Before anyone mentions it, I'm aware thatthis PR in the typescript repo exists and would solve all problems below if merged, but It's been around since 2018 and it's unsure when or if it is going to get merged, so I still think this discussion is worth having.

Example 1: Non-exported types in 3rd party packages

When integrating with 3rd party typescript packages, it's unfortunately common for packages to not expose all the types the consumer may need. While this is a fault in those packages, I think we have to be pragmatic and acknowledge that this is a common enough problem to justify a workaround.

Let's imagine the packageexample-package that doesn't export a generic constraint type:

typeGenerics={a:unknown;b:unknown;c:unknown;};exporttypeFoo<GextendsGenerics>={generics:G;}

This is what it would look like to consume this package:

Strategy 1: Usingnever as generic constraint

Usingnever makes the constraint too specific and the function now only acceptsFoo<never>

import{Foo}from"example-package";declarefunctionuseNever<TextendsFoo<never>>(arg:T):void;useNever({generics:{a:123,b:123,c:123}});//            ^ Type '{ a: number; b: number; c: number; }' is not assignable to type 'never'.(2322)

Strategy 2: Usingunknown as generic constraint

Usingunknown requires us to reconstruct the type used for the generic constraint while usingunknown everywhere.

declarefunctionuseUnknown<TextendsFoo<{a:unknown,b:unknown,c:unknown}>>(arg:T):void;

This actually works. Neither Typescript or typescript-eslint yields errors, but this is unnecessarily verbose when all I want is to tell typescript thatuseUnknown should accept any form ofFoo. It's also brittle, since I have to redefine implementation details of the 3rd party package. If the package refactors their internal type definitions, I will have to refactor my code.

Strategy 3: Usingany as generic constraint

Finally let's try usingany. It gives us the best developer experience. It applies the generic constraint we want, is terse and not brittle, and without yielding any typescript errors. However, unfortunately, typescript-eslint complains if you haveno-explicit-any enabled.

declarefunctionuseAny<TextendsFoo<any>>(arg:T):void;

Example 2: The same problem can still occur even if you own all the type definitions

Let's say we have this builder pattern:

typeGenericKeys="a"|"b"|"c"interfaceGenerics<Aextendsunknown[],Bextendsunknown[],Cextendsunknown[]>{a:A,b:B,c:C,}declareclassBuilder<ValuesextendsGenerics<unknown[],unknown[],unknown[]>>{readonlyvalues:Readonly<Values>;add<AddToextendsGenericKeys,Added>():Builder<{[KinGenericKeys]:KextendsAddTo ?[...Values[K],Added] :Existing[K]}>}constbuilder=newBuilder<{a:[],b:[],c:[]}>().add<"a",number>().add<"a",string>().add<"b",boolean>().add<"b",null>()// ^ Builder<{ a: [number, string]; b: [boolean, null]; c: []; }>

Now we want to define some functions that accepts generic variants of this Builder type. Observe that if we try the same strategies as in Example 1, we'll experience the exact same problems:

Strategy 1: Usingnever as generic constraint

declarefunctionuseNever<BextendsBuilder<never>>(builder:B):void;useNever(builder);// ^ Argument of type 'Builder<{ a: [number, string]; b: [boolean, null]; c: []; }>' is not assignable to parameter of type 'Builder<never>'

Strategy 2: Usingunknown as generic constraint

Once again, this works, but is as verbose and brittle as mentioned in Example 1.

declarefunctionuseUnknown<BextendsBuilder<Generics<unknown[],unknown[],unknown[]>>>(builder:B):void;useUnknown(builder)

Strategy 3: Usingany as generic constraint

Once again,any as generic constraint is the winner:

declarefunctionuseAny<TextendsFoo<any>>(arg:T):void;

eslint test cases

Fail

// Using `any` outside of generic constraints should still be an errorconstfoo:any=123;functionfn(arg:any){}

Pass

// But using `any` inside a generic constraint should be validfunctionfn<Textendsany>(arg:T){}

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancement: plugin rule optionNew rule option for an existing eslint-plugin rulepackage: eslint-pluginIssues related to @typescript-eslint/eslint-pluginwontfixThis will not be worked on

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


      [8]ページ先頭

      ©2009-2025 Movatter.jp