Preview:```ts
var map = new Map<number, string | undefined>()
map.set(1, "a")
map.set(2, undefined)
map.set(3)```
You can choose specific lines to embed by selecting them before copying the link.
26 messages · Page 1 of 1 (latest)
Preview:```ts
var map = new Map<number, string | undefined>()
map.set(1, "a")
map.set(2, undefined)
map.set(3)```
Not everything that is possible to do in JS, is a good idea to do.
Map.set NEEDS a key and a value. The fact that you're allowing undefined doesn't change that.
It does work in the JS runtime
And also in JS I don’t believe there is a way for the callee to know whether the second parameter was explicitly passed as undefined, or omitted at the call site
So it’s not technically needed for that reason
Yeah, TypeScript does need it tho, since Map is defined as <K,V>Map
You could write a extension on Map
Or a custom map
You can also declaration merge into it.
But again, just because you can do it in JS, doesn't mean it's a good idea.
Typescript shouldn’t be imposing opinions like that though, it should just be a matter of record for how JS actually works
Save the opinions for linters etc
What you're saying is that the type declaration of any function from the standard library that doesn't crash when you don't give in a parameter should mark that parameter as optional. You can call parseInt with no argument to get NaN but marking the argument as optional is dumb.
Especially since the example given explicitly says value needs to be a string or undefined
It's not "typescript's opinion", the types have been written while accounting for the intended way to use APIs, not to encode things that happen to work because JS is permissive. The ecmascript spec doesn't define the value parameter as optional: https://tc39.es/ecma262/multipage/keyed-collections.html#sec-map.prototype.set
class Mep {
_mep: Record<PropertyKey, string | undefined> = {}
set(key: PropertyKey, value?: string | undefined) {
this._mep[key] = value || undefined
}
}
const map = new Mep()
map.set(1, 'a')
map.set(2, undefined)
map.set(3)
Now you can.. yay
You don't even need to do that, just declaration merge into it.
interface Map<K, V> {
set(key: undefined extends V ? K : never): Map<K, V>
}
const map1 = new Map<string, number>()
map1.set('foo', 42)
map1.set('bar') // should error
const map2 = new Map<string, number | undefined>()
map2.set('foo', 42)
map2.set('bar') // should pass
!ts
interface Map<K, V> {
set(key: undefined extends V ? K : never): Map<K, V>
}
const map1 = new Map<string, number>()
map1.set('foo', 42)
map1.set('bar') // should error
// ^^^^^
// Argument of type 'string' is not assignable to parameter of type 'never'.
const map2 = new Map<string, number | undefined>()
map2.set('foo', 42)
map2.set('bar') // should pass
But yes, don't do this, it's bad.
Ah, neat! But bad yeah
in the ecmascript spec, arguments are neither required nor optional. the spec will always be explicit about what should occur if a parameter is undefined. e.g. radix in parseInt, or when passing nothing to Object.keys() - its just the nature of the language
of course typescript should not include declarations where the result will cause an error e.g. Object.keys(undefined) or Object.keys()
but in the case of map.set(1) vs map.set(1, undefined) -- not only does the command not crash, it produces the desired behaviour because the implementation is spec-compliant
It is very explicit about arguments being optional: they are in brackets, eg:
https://tc39.es/ecma262/multipage/keyed-collections.html#sec-map.prototype.foreach
thisArg is explicitly optional.
Unless otherwise specified, [a function's length] is the number of required parameters shown in the subclause heading for the function description. Optional parameters and rest parameters are not included in the parameter count.