Skip to content
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

Set.has typing #42641

Closed
mbelsky opened this issue Feb 4, 2021 · 5 comments
Closed

Set.has typing #42641

mbelsky opened this issue Feb 4, 2021 · 5 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@mbelsky
Copy link

mbelsky commented Feb 4, 2021

lib Update Request

Missing / Incorrect Definition

The Set.has method's type expects that value type is typeof T:

https://github.com/microsoft/TypeScript/blob/master/src/lib/es2015.collection.d.ts#L43

And this definition makes safe-code uncompilable.

Sample Code

const set: Set<number> = new Set([1, 2, 3, 4])

set.has(5)
set.has('error?')
        ^^^^^^^ Argument of type 'string' is not assignable to parameter of type 'number'.

sandbox

Documentation Link

https://tc39.es/ecma262/#sec-set.prototype.has


Well what do you think about replacing this method definition with this:

    has(value: unknown): boolean;
@MartinJohns
Copy link
Contributor

MartinJohns commented Feb 4, 2021

Checking the existence of a value that can't be present (according to the type system) should be an error. Why would you want it to be different?

In your local code you could just use 'error' as any.

@mbelsky
Copy link
Author

mbelsky commented Feb 4, 2021

I've came with this issue because now users need to add unnecessary type guards to make a safe operation in their code. An example:

const set: Set<number> = new Set([])

const list: unknown[] = [1, '2', {}]

list.map((item) => {
    if (typeof item === 'number') { // you don't need this type guard, but the code won't compile without that
        return set.has(item)
    }

    return false
})

@jgbpercy
Copy link

jgbpercy commented Feb 5, 2021

Right, well as Martin says, either do:

const set: Set<number> = new Set([])

const list: any[] = [1, '2', {}]

list.map((item) => {
    return set.has(item)
})

Or:

const set: Set<number> = new Set([])

const list: unknown[] = [1, '2', {}]

list.map((item) => {
    return set.has(item as any)
})

...if you want do something like your example code without adding any code that will be present at run time. Although if it were me I'd generally write the version with the type guard! unknown is a deliberately restrictive type that's designed to flag these sorts of things as errors if you're not asserting or guarding to a more specific type, any is designed to be permissive.

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Feb 5, 2021
@RyanCavanaugh
Copy link
Member

Ideally this would be a bivariant parameter; see #14520. Array#includes has the same problem.

The current behavior is the best available compromise. You're free to add the unknown overload to your code if you want to opt in to the less-specific version.

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

5 participants