total-typescript / ts-reset

A 'CSS reset' for TypeScript, improving types for common JavaScript API's
https://www.totaltypescript.com/ts-reset
MIT License
7.74k stars 117 forks source link

Make WeakSet.has() and WeakMap.has() accept `unknown` and assert type. #180

Open DeepDoge opened 7 months ago

DeepDoge commented 7 months ago

It's annoying when you have a value with unknown type and can't use it inside .has() or .delete() function. Which doesn't make sense at all. unknown should be allowed and also .has() should assert type.

Solution:

interface WeakSet<T extends WeakKey> {
    delete<V>(value: V & (unknown extends V ? unknown : T)): value is T
    has<V>(value: V & (unknown extends V ? unknown : T)): value is T
}

Result:

interface Foo {
    foo: string
}

interface Bar {
    bar: string
}

const foo: Foo = { foo: "foo" }
const bar: Bar = { bar: "bar" }

const unknownFoo: unknown = foo satisfies Foo

const fooSet = new WeakSet<Foo>()

if (fooSet.has(unknownFoo)) {
    unknownFoo.foo // Foo
}

if (fooSet.has(foo)) {
    foo.foo // Foo
}

if (fooSet.has(bar)) { // Error: No overload matches this call.
    bar.foo // Foo
}

Same thing can be done for the WeakMap

russelldavis commented 5 months ago

The type predicates in these definitions are unsafe, see https://github.com/total-typescript/ts-reset/pull/125#issuecomment-1926133512