-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Allow wrapped values to be used in place of primitives. #2361
Comments
What specific use cases do you have in mind here? |
We had a similar discussion a long time ago -- that the rules for the math operators should be written in terms of the |
Is there a public thread somewhere I can check out?
What issue are you referring to? |
It was an internal discussion before we went public.
> var x = new Date();
undefined
> x.valueOf()
1426616370842
> x + x
"Tue Mar 17 2015 11:19:30 GMT-0700 (Pacific Daylight Time)Tue Mar 17 2015 11:19:30 GMT-0700 (Pacific Daylight Time)"
> x - x
0
> x + 0
"Tue Mar 17 2015 11:19:30 GMT-0700 (Pacific Daylight Time)0"
> x - 0
1426616370842
> x * x / x
1426616370842
> x * x + x
"2.0352342695543988e+24Tue Mar 17 2015 11:19:30 GMT-0700 (Pacific Daylight Time)" 😢 |
Oh man ... I didn't know that. |
What if |
Would like to see this as e.g. a change to the spec + implementation that specifies exactly how this might work |
I need this one 👍 |
Hi, This is just an other use-case for this issue and I'm hoping that there will be a solution. |
@RyanCavanaugh I noticed that in the es6 typings, the type BoxedValue<T> = {
valueOf(): T;
}
type DefaultValue<T> = {
[Symbol.toPrimitive]?(hint: "default"): T;
}
type Operand<T> = T | BoxedValue<T>;
type StrictOperand<T> = T | (BoxedValue<T> & DefaultValue<T>); 4.19.1 The *, /, %, –, <<, >>, >>>, &, ^, and | operatorsThese operators require their operands to be of type Any, the Operand type, or an enum type. Operands of an enum type are treated as having the primitive type Number. If one operand is the
4.19.2 The + operatorThe binary + operator requires both operands to be of the StrictOperand type or an enum type, or at least one of the operands to be of type Any or a Operand type. Operands of an enum type are treated as having the primitive type Number. If one operand is the
|
@RyanCavanaugh I did (an extremely hacky) implementation and it does seem to work master...icholy:master |
In my own code I'll probably start using something like this: class Time extends Date {
[Symbol.toPrimitive](hint: "default"): number;
[Symbol.toPrimitive](hint: string): string | number {
switch (hint) {
case "number":
return this.valueOf();
case "string":
return this.toString();
default:
return this.valueOf();
}
}
} |
Why is this still open? I think that after 7 years at least a resolution "yes, we'll implement" or "no, won't do" should be defined, no? I believe that it should be implemented, especially because it is valid JS and, by the number of issues referencing this one and the amount of comments presenting use cases, it's clearly seen as a bug in Typescript On my case, I'm using mongoose's Decimal128 type, which converts to number normally, but since typescript won't let me I have to add a bunch of As far as I understand from this thread, the only problem with this new feature is the Date object, which is fair. So, if it is possible to add an exception to the rule, not allowing a But, if adding an exception is not possible, then I think that at least this issue should be closed with a "won't implement because ", because it's 7 years already, letting this without even a resolution with clearly a lot of people considering this as a bug it's a bit absurd |
This is a confusing issue for even simple BigInt usage: function f0(int: BigInt): BigInt {
return BigInt(int) // Error: Argument of type 'BigInt' is not assignable to parameter of type 'string | number | bigint | boolean'.(2345)
}
console.log(f0(BigInt(1))) // OK: 1 A workaround is: function f1(int: BigInt): BigInt {
return BigInt(int.valueOf()) // OK
}
console.log(f1(BigInt(2))) // OK: 2 |
The problem here is that The real workaround here is: function f0(int: bigint) { // The return type is already "bigint"
return BigInt(int)
}
console.log(f0(BigInt(1))) |
I'm using primitive type coercion in my Java Runtime Environment emulation, to provide the same experiences for TS users like there are for Java users. In the Boxing and Unboxing chapter I explain valid use cases, which none-the-less are defeated by the TS compiler. For a typical use case check the implementation of the |
@Llorx you are talking about wrapper objects, right? As @mike-lischke says, they can be used to box/type-cast values as used in java and C# but they are not implemented in the same way in JS. None of the wrapper objects are meant to be used with the
This issue is about const def = {
toString () { return "def" },
valueOf () { return 123 }
}
console.log (
// When String is called as a function rather than
// as a constructor, it performs a type conversion.
("abc" + String (def)), // <- "abcdef"
// In TypeScript you will get an error below saying
// Operator '+' cannot be applied to types 'number' and '{ toString(): string; valueOf(): number; }'.
(1 + def), // <- 124
) The use of const dateA = new Date ("1981-04-10T10:00:00")
const dateB = new Date ("1981-04-10T12:00:00")
console.log ((dateB - dateA) / 1000 / 60 / 60 + " hours") // <- "2 hours"
/* TypeScript Errors:
The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
*/ PS. Makes you wonder how TypeScript can be a super language of JS, when it has never fully supported JS. console.log (
"Today's date is " + new Date())
// "Today's date is Thu Dec 29 2022 19:30:56 GMT+0100 (Central European Standard Time)" |
No updates on this whatsoever?! It's a highly requested feature and is almost 8 years old. Thanks. |
Our iteration plans are public and you can check them to see if things are in there; no hints are needed. I don't think it's particularly likely to happen soon given that doing these operators on wrapped values is somewhat rare, and In general if an issue is old, it's probably old for reasons that can be found in the thread. Language design is not a first-in first-out queue and things which don't seem tractable "for now" are generally left open since people complain more if we close them. |
Also a +1 for this. The compiler should recognize the presence of |
I agree that JavaScript works in weird ways (#2361 (comment)), but I have to add though that now with the former explanation, implicit type coercion in JavaScript has its traps, and when you have to understand the specification to figure out how code like |
This is very frustrating. I have a custom "dynamic properties" implementation where objects can hold various values or collections of them (for purposes like change events, de/serializing, view delegates, etc). These dynamic property objects can easily return whatever type they actually hold, including primitives, using I don't understand the argument that it's not a "commonly" used feature... those are rather major features of the JS/ECMA. This is why they can be overridden, right? We don't have direct access to declare interface StringProperty {
valueOf(): string;
[Symbol.toPrimitive] (h: 'default') : string;
} How to make this work? Seems basic to me, but clearly I'm missing whatever the underlying issue is. But I'd like it to work... :) I'm OK with all the extra TS declarations and annotations required to help ensure type safety and IDE hinting. Return on investment. But when it starts requiring actual code workarounds... that's too much IMHO. Thank you for your consideration. |
For reference on lack of TS support, see: microsoft/TypeScript#2361
For reference on lack of TS support, see: microsoft/TypeScript#2361
…pe (#6694) * Group schema normalization tests into suites. * Allow 'counter' in schema parser. * Test schema parser with 'counter'. * Add Counter class. * Implement creating a counter on Realm object. * Add implementation to Counter. * Expose Core's 'add_int()'. * Add object property getter. * Export types. * Test create and access via 'realm.create()' (input: nums and Counter). * Test create and access via 'realm.create()' (input: collections). * Test create and access via collection accessors. * Test updating the counter value. * Test updating the Realm object counter property. * Move common logic to shared functions. * Test updating the Realm object collection property. * Test returning different reference for each access. * Test throwing when updating by non-integer. * Change how a counter should be defined in the schema. We originally decided on using 'counter' as its own primitive type (defined e.g. "type: 'counter'"). We have updated this to now use a 'presentation' type (e.g. "type: 'int', presentation: 'counter'"). * Rename test property. * Add 'skips' to tests that await implementation. * Add 'presentations' to the stored sdk-specifc schema info. * Add 'isCreating' argument to object property setters to disallow certain operations for counter. * Test throwing when resetting via property setter. * Test throwing outside transaction. * Test throwing if setting regular int to counter. * Test throwing if invalidated object. * Extend 'Unmanaged' with Counter and allowing number. * Throw custom message if getting value on invalid object. * Implement 'Counter.set()'. * Disallow counter as mixed. * Test throwing if used as mixed. * Test filtering. * Fixing returning null if counter is null. * Remove support for collections of counters. * Test updating Realm object counter prop via UpdateMode. * Fix Unmanaged type for Counter. * Add Unmanaged RealmSet. * Update API docs. * Remove implicit coercion to number due to TS not supporting it. For reference on lack of TS support, see: microsoft/TypeScript#2361 * Test 'Realm.schema'. * Replace 'Object.defineProperty' with Symbols. * Add CHANGELOG entry. * Change default value in test to non-zero. * Test throwing when setting 'Counter.value' directly. * Make 'describe' test names lowercase. * List what is not supported onto the 'Counter' API docs. * Move 'expectCounter' to common file. * Remove link to API ref docs in CHANGELOG if doing RC release. * Refactor a test to use `map` instead of for-loop Co-authored-by: Kræn Hansen <[email protected]> * Update error message from 'floating point' to 'decimal'. * [12.10.0-rc.0] Bump version (#6696) Co-authored-by: elle-j <[email protected]> * Prepare for vNext (#6697) Co-authored-by: elle-j <[email protected]> * Update formatting. * Update test variable name. * Add links in CHANGELOG. Co-authored-by: Kenneth Geisshirt <[email protected]> * Update API docs formatting. * Update initial value in test from 0 to 10. * Refactor similar tests into for-loop. * Point to Core v14.10.0. * Update Counter API docs. * Remove redundant comment. * Refactor logic allowing/disallowing setting a counter. * Update type name w/o abbreviation Co-authored-by: Kræn Hansen <[email protected]> * Fix CHANGELOG after merge. --------- Co-authored-by: Kræn Hansen <[email protected]> Co-authored-by: Yavor Georgiev <[email protected]> Co-authored-by: elle-j <[email protected]> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Kenneth Geisshirt <[email protected]>
This comment was marked as off-topic.
This comment was marked as off-topic.
@RyanCavanaugh could you briefly comment on @icholy's propostal described in this comment and implemented at this branch (not hacky at all) ? It seems like a sensible approach to me as it manages to handle Date. Date type is detected to be My impression is that most wrapped primitive use-cases are number based as opposed to other primitive types. Considering this and also the fact that Date is quirky for addition, there is an even simpler apprach that only handles the number case. In this case, we just make sure to keep Date out: adding Dates would still not be allowed. Kindly clarify what would be needed for such a proposal to be considered. Thank you. |
It would be nice if an arithmetic operation could allow using the wrapped number because it's
valueOf()
method returns the expected primitive type.This can be generalized to: if type
T
is expected, then a value that implements the following interface can be used.The text was updated successfully, but these errors were encountered: