Open ghost opened 3 years ago
Alternatively, TypeScript should handle the [Symbol.toPrimitive]
 method as special, and maybe also valueOf()
and toString()
, excluding the implicit valueOf()
and toString()
 methods inherited from Object.prototype
.
@ExE-Boss That is a possibility, but there are three problems that I can see with that approach, the first being that the proposal that I mentioned does not use them, so TS would still need another form of exposing overloads, should it ever reach stage 4, the second being that one may not want the Symbol.toPrimitive
to decide whether or not an object has operations permitted on it, actually, I think that may be backwards-incompatible, as it would now allow operations on arbitrary objects where previously they could not have been done, lastly, it stops the entire primitive typing idea that I tried to introduce, as it would only apply to objects.
If js/ts support operator overloading, a lot of features would be possible, some code could be rewritten simplify. And some missing lib like python's sympy
could be implement.
Looking forward to have this promising features as soon as possible.
@liudonghua123 I don't think that you understand what I'm proposing here? I'm merely asking for typing for this idea, your comment would be more suited to the ES proposal that I had linked, as they are proposing a real runtime feature.
@CrimsonCodes0 I think the syntax should look more like the c++ syntax
type Position = {
x: number;
y: number;
};
function operator+(rhs: Position, lfs: number): Position {
return {
x: position.x + nbr,
y: position.y + nbr
}
}
let position1: Position = {
x: 1,
y: 2
};
let position2: Position = position1 + 2;
// result: { x: 3, y: 4 }
// Under the hood it would look more like this
let position2: Position = operator+(position1, 2);
Under the hood, the compiler will create a unique function name for the addition operator function. The addition operator function should always return a value that is the same type as rhs argument. The addition operator function can also be used for the addition assignment operator if all the operator functions will return a new value. It would be more like a type of extension function.
I think personally we should having operator overloading part of a class, it would make it messy and also the benefit of having them as functions they can be declared locally to a specific scope.
There should be predefined interfaces for every operator that can be overloaded so they can be targeted in generic function, for example:
function sum<T extends IAdtionOpertor>(items: T[]): T {
let [sum, ...restItems] = items;
for (const item of restItems) {
sum += item;
}
return sum;
}
let positions: Position[] = [
{x: 1, y: 1},
{x: 2, y: -1},
{x: 3, y: 3},
];
let positionSum = sum(positions);
// results: { x: 6, y: 3};
@joseDaKing Sorry, you seem to lack context, and haven't read my issue at all... I'm asking for operator overloading declarations, just like type declarations, they're not implementations.
Note that I'm aiming to follow these points:
This wouldn't change the runtime behavior of existing JavaScript code And This could be implemented without emitting different JS based on the types of the expressions This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
You ask for
function operator+(rhs: Position, lfs: number): Position {
return {
x: position.x + nbr,
y: position.y + nbr
}
}
Yet, I explicitly said,
No, I am not asking for operations to be replaced with functions or such, like many other issues have asked for, I am merely asking for a way to show what types that an operation will yield when performed between two types.
Please, read the entire issue; if you had, you would've seen that I mentioned how ECMAScript itself is already implementing this: https://github.com/tc39/proposal-operator-overloading.
So this really isn't a thing? I found these old issues closed by MS, so I thought maybe it was already added; this guy has a fairly complete description of the functions and rules...
https://github.com/microsoft/TypeScript/issues/5407
This would be anice feature to have been added to Typescript which has the type information required.... adding to JS is a lot of overhead in the javascript engine I'd think...
I really think the operator overloading should be detached from the "primitive" proposal, since "primitives" can be implemented using, e.g., branded types.
A more conservative operator overload syntax will perhaps be something like:
declare operator "+"(lhs: Foo, rhs: Foo): Foo;
Some of my (current) use cases include:
1 + true
Work with packages like bignumber.js and dinero.js would improve significantly.
A recent example from one of my projects;
These types of "custom primitives" help type check mixing units like seconds / milliseconds or degrees/radians etc.
type milliseconds = number & { readonly unit: unique symbol };
type seconds = number & { readonly unit: unique symbol };
declare global {
interface Date {
getTime(): milliseconds;
}
interface DateConstructor {
now(): milliseconds;
}
}
export function msToS(ms: milliseconds): seconds {
return (ms / 1000) as seconds;
}
export function sToMs(s: seconds): milliseconds {
return (s * 1000) as milliseconds;
}
const time = Date.now(); //milliseconds
console.log(msToS(time)); // OK
// console.log(sToMs(time)); // Argument of type 'milliseconds' is not assignable to parameter of type 'seconds'.
const delta = time - Date.now(); // milliseconds - milliseconds => number rather than milliseconds
console.log(msToS(delta)); // Argument of type 'number' is not assignable to parameter of type 'milliseconds'.
It would be handy to declare that milliseconds + milliseconds
=> milliseconds
etc. or that kilometers / hour
=> kph
.
Additionally, it would be nice to allow implicit casts from these types to number
which can be achieved by making unit
optional, but the downside to that is that its easy to accidentally convert between types. Making unit
not optional (as above) means that you need to explicitly recast foo
back to seconds
, which at least shows maybe you know what you're doing (as in msToS
and sToMs
)
type milliseconds = number & { readonly unit?: unique symbol };
type seconds = number & { readonly unit?: unique symbol };
const foo = 1 as seconds;
msToS(foo); //error
msToS(foo + 0); //ok
Being able to declare that seconds + number => seconds
would correctly cause the last line above to show an error.
Suggestion
đ Search Terms
Operators, operator types, operator overloading
â Viability Checklist
My suggestion meets these guidelines:
â Suggestion
Introduce a way to show what types can/cannot be gained from using an operator on a value of a type.
No, I am not asking for operations to be replaced with functions or such, like many other issues have asked for, I am merely asking for a way to show what types that an operation will yield when performed between two types.
Currently, there is no way to describe operator types in TS.
đ Motivating Example
I don't have an idea for the syntax, but I'll introduce a partial syntax to showcase the idea here.
Let's imagine that TS allowed us to declare primitive types via, say, a
primitive
keyword:This is already valid TS:
This suggests that TS already has the notion of overloaded operators that I have suggested.
Note that operators may never have a body, as TSC is not allowed to emit runtime code for the operations.
Now, TS doesn't presently allow us to declare primitives, but operations between objects always throw the error:
And generally, yes, that is a good thing, but is it always? Take this example:
I know that the ES abstract
ToPrimitive
will be called on both of these objects, resulting in the primitive string value contained within. Run the code yourself, it will result in"foobar"
, and we know this, so let's tell TSC that too! But, instead of the typestring
being the result of the concatenation operator, we get this:Let's say that we were using a type that becomes a number, ex:
WebAssembly.Global
:That string example could could now be something like this:
Of course, as with anything else, this can be misused, but it's no worse than the already existing
2 + ""
semantics.đ» Use Cases
This can likely solve issues such as https://github.com/microsoft/TypeScript/issues/28682 solely via user-implemented types!
If we could declare opaque types, such as those mentioned in https://github.com/microsoft/TypeScript/issues/15408 or https://github.com/microsoft/TypeScript/issues/40075, one could do stuff like, say, creating a NaN type, and stricter number types, all without runtime overhead and erasable types.
Toss in throw types and we can get some good error messages out of it too:
Another use, working with pointers into, say, WebAssembly memory, it usually makes no sense to do something like raising it to an exponent, and this could allow us to scope what operations are permitted. Before:
after:
There will have to be some TSC enforced rules in order for it to actually be useful, ex: the return type of + must extend
number | string | bigint
, because nothing else could be possible.If the primitive type idea is too radical, it could be completely decoupled from the operator overloading, so that I may perform my object arithmetic with the safety of TS. :)
Also, eventually, TS may have to implement this anyways: https://github.com/tc39/proposal-operator-overloading