-
Notifications
You must be signed in to change notification settings - Fork 415
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
URLSearchParams API should accept numbers for values #1568
Comments
I personally would prefer not to see this change. The automatic coercion of non-strings to strings that exists in many DOM apis is very implicit and I like that typescript is more strict about it, instead forcing me to explicitly |
I think use |
@nmain @CaptainInPHW I truly believe that it doesn't make any sense to justify anything in this thread with a personal preference(s). If you would like to open a discussion please provide solutions to real word use cases I mentioned above |
I actually agree with @artyil const url1 = new URLSearchParams({ foo: false, bar: '1' }) // <- FAILS
const url2 = new URLSearchParams({ foo: 'false', bar: '1' }) // <- OK The spec of URLSearchParams doesn't specify the value type of that object, Forcing code like this, is too strict, and weird: const url1 = new URLSearchParams({ foo: false.toString() }) So it should support string|number|boolean |
I largely agree with @nmain that the automatic coercion of non-strings to strings that exists in many DOM apis is very implicit and typescript should NOT cater to those implicit coercions. I'm not sure that applies here. Here we are creating a new URL object. I think we can agree that a URL is a specific type of string so passing nonString values to the constructor, append and set methods would be like calling a urlify method. You are explicitly asking for a standardized way of converting values to a url type value (string). Very similar to JSON.stringify, yes things are being casted but that's the core functionality of the method. The underlying implementation just uses template literals to cast to a string. I'd be happy to make the pr to accept template-literable types as values here. |
AFAICS, string coercion is not standardised as part of
https://url.spec.whatwg.org/#idl-index I would prefer const myUrlSearchParams = (init: Record<string, string | number | boolean>) =>
new URLSearchParams(init as Record<string, string>) Maybe coercion will be added to the standard in the future, at which point I would expect the types to also be updated. Not all string coercion is desired (e.g. objects/functions, |
@RyanCavanaugh I don't think this is a bug, and thus the issue should be closed: https://url.spec.whatwg.org/#ref-for-dom-urlsearchparams-urlsearchparams specifies that If typings should also represent coercion states, we could type essentially all Web APIs as taking |
There are many valid DOM Api's that takes "any" as a valid argument and it bother me on so many levels that TypeScript treats it so strictly. Like: it should be totally fine to do: Take this bit mask example that takes a bunch o boolean and turn it into a number function createMask (...args: boolean[]) {
var nMask = 0, nFlag = 0, nLen = args.length
for (nFlag; nFlag < nLen; nMask |= args[nFlag] << nFlag++)
return nMask
}
const isLoggedIn = true
const hasReadPermission = true
const hasWritePermission = false
createMask(isLoggedIn, hasReadPermission, hasWritePermission) // 3 TS is complaining that nFlag isn't a number, but it's totally fine to do bitwise operators on boolean to, cuz in the end boolean is just a 1 or a 0 The WebIDL dose so many things to converting |
@jimmywarting Typescript treats USVString as a string because it is a string. It also treats ByteString or DOMString as a string, because they are also strings. There is nothing special about USVString in regards to type coercion. All types except Symbol can be coerced into USVString/DOMString/ByteString in WebIDL. I don't think typescript should represent this though, as the point of it is to prevent runtime coercion. So I don't think you agree with me, because I think this issue is invalid and should be closed as working as intended 😄 |
Little bit of a useless example, since nobody here is trying to make the argument it should accept |
Can we bring back this discussion? I think it's enough for it to accept primitives that we know can be converted to strings, and also any object that implements the |
I can't believe the TS team seriously went with this decision. |
really confusing, may we get it? |
The way to fix this is actually to update TypeScript-DOM-lib-generator with overriding types. Here's a test case of what I'd expect to work: export const carQuery = new URLSearchParams([
["query", "suv"],
["new", true],
["accidents", false],
["miles", 42_000],
]);
carQuery.set("used", true);
carQuery.append("year", 2023);
carQuery.append("year", 2024);
let str: string | null, strs: string[];
str = carQuery.get("query");
strs = carQuery.getAll("year");
for (const [key, value] of carQuery) {
str = key;
str = value;
}
for (const [key, value] of carQuery.entries()) {
str = key;
str = value;
}
for (const value of carQuery.values()) {
str = value;
}
carQuery.forEach((value, key) => {
str = key;
str = value;
}); |
@DanielRosenwasser what is the general policy for when lib files should encode coercion into types? I really think allowing primitive coercion here is confusing. One may think that the value Numbers are iffy, but Booleans seem just outright confusing. |
It is usually driven by practical usage in JS and what the tradeoffs in safety are - usually on a case-by-case basis. Honestly, I'm kind of surprised that booleans are confusing here. I can see that |
I think
and
As a user I may well suspect either I can get behind numbers, because they are unambiguously encoded - but I've often seen folks do:
As a user not super in my depth I could get confused and think that the initializer form of What about strings and numbers only? |
Strings and numbers only is what @RyanCavanaugh recently recommended for a PR attempt, so I'm okay with that as a first step. |
Actual implementation of
URLSearchParams.constructor
andURLSearchParams.prototype.set
methods in the browsers will accept any value as value, including number or Integer.TLDR:
new URLSearchParams([['foo', 1]])
andnew URLSearchParams().set('bar', 1)
should compile without TS errorsIt works in current implementation, because numeric value will be coerced to actual
USVString
value, which is astring
.Unfortunately, current TS definition of type
URLSearchParams
inlid.dom.d.ts
will only allow strings for above mentioned setters. Below are the definitions.I can understand that the definitions are mirroring the W3C specifications but I do believe it is not the expected experience for developers.
The actual definition enforces developers to make unnecessary operations (helpers / utils / map-reduces) to overcome TS errors 👋
Can we reconsider to allow values to be numeric to mentioned setters? This won't break any current bahavior or actual implementation.
Even as per MDN examples constructor and URLSearchParams.prototype.set it should work as expected:
Which works in browser but doesn't work in TS.
TypeScript Version: 3.4.3
output with tsc typescript@next
Search Terms:
Typescript URLSearchParams invalid constructor
Typescript URLSearchParams won't accept numbers as value
Typescript URLSearchParams won't accept numeric values
standard lib.dom.d.ts URLSearchParams allow numbers as values
Typescript URLSearchParams coercion number to string
Code
Expected behavior:
All above cases should work, because numeric values are being coerced to strings
Actual behavior:
TS won't compile due to URLSearchParams definitions only accept string for value
Playground Link:
link
Related Issues:
microsoft/TypeScript#15338
The text was updated successfully, but these errors were encountered: