Skip to content

Add an option not to use implicit any when I create an empty container (for example [], new Set() or new Map()) #61638

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

Open
6 tasks done
DonaldDuck313 opened this issue May 1, 2025 · 2 comments

Comments

@DonaldDuck313
Copy link

DonaldDuck313 commented May 1, 2025

🔍 Search Terms

implicit any containers

✅ Viability Checklist

⭐ Suggestion

Currently, new Map() has type Map<any, any> and new Set() has type Set<any>. I suggest to add an option so that instead new Map() has type Map<never, never> and new Set() has type Set<never>. This should work because Set<never> is assignable to any other set type, and same for maps.

Note that this needs to be an option, otherwise it could break existing code.

📃 Motivating Example

I ran into this issue with code similar to the following:

declare function returnsNullableMap(): Map<number, number> | null;

const myMap = returnsNullableMap() ?? new Map();
myMap.set("", "");    //This compiles but shouldn't

The last line is obviously a mistake since the map contains numbers, not strings. But since new Map() has type Map<any, any>, so does myMap, and the last line compiles.

💻 Use Cases

  1. What do you want to use this for?

I would like to completely avoid the any type in my code, and the fact that the code above actually does contain any is not immediately obvious. I use existing options such as noImplicitAny and useUnknownInCatchVariables to avoid any sneaking in at other places, but apparently any can still sneak in in this case. By the way, I would have expected noImplicitAny to cover this case as well, but apparently it doesn't.

  1. What workarounds are you using in the meantime?

In the specific code above I can use const myMap = returnsNullableMap() ?? new Map<never, never>(); and it works fine, myMap has the correct type (Map<number, number>). Note that in my actual code my types are much more complicated than just number, so using never saves a lot of typing compared to using the actual type. In this simplified example however new Map<number, number>() would have worked just as well.

  1. What shortcomings exist with current approaches?

The main issue is that if I forget the <never, never> (or a similar workaround), my code will contain the any type without making it immediately obvious, allowing mistakes like in the example above. Another minor annoyance is that new Map<never, never>() is more verbose than just new Map(), but I can live with that, what I'm more concerned about is any types sneaking into my code where I don't want them.

@RyanCavanaugh
Copy link
Member

This is already an option.

Currently if I do let a = [];, a will be of type any[].

It's an evolving array, not really an any[]. You will get an error under noImplicitAny if you do any access that actually produces an any value, e.g.

function foo(arr: unknown[]) { }

let a = [];
foo(a); // Implicit any error

If you want this behavior in Set or Map, write a new overload in a declaration merge:

export { }
declare global {
    interface SetConstructor {
        new(): Set<never>;
    }
    interface MapConstructor {
        new(): Map<never, never>;
    }
}

const m = new Set(); // m: Set<never>

@DonaldDuck313
Copy link
Author

You're right, it already works as it should for arrays. Your workaround for sets and maps is better than mine, but it would still be nice to have a tsconfig.json option for that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants