-
-
Notifications
You must be signed in to change notification settings - Fork 184
Description
One of the few things remaining that can't be mapped from TS to Lua is operator overloads. Since it seems unlikely JS will support syntax for overloading anytime in the near future, I believe we should add some sort of alternative mapping to allow these to be accessed. This is especially important for environments that provide types with functionality that can only be accessed this way (such as math types like vectors).
There's a few ways this could be achieved, so before actually working on a PR, I'd love to get feedback on what the most appropriate way to implement this would be. Here's a few proposals:
Option A: Helper Types
Use helper types, provided by TSTL to allow declaring operator mappings.
//Provided by TSTL as helper
declare type LuaAdd<A, B, R> = { (a: A, b: B): R; readonly __luaAdd: unique symbol; }
declare type LuaAddMethod<A, B, R> = { (this: A, b: B): R; readonly __luaAdd: unique symbol; }
declare class Vector {
add: LuaAddMethod<Vector, Vector, Vector>;
static add: LuaAdd<Vector, Vector, Vector>;
}
declare const addVectors: LuaAdd<Vector, Vector, Vector>;
// All transpile to 'a + b'
declare const a: Vector, b: Vector;
const c = a.add(b);
const d = Vector.add(a, b);
const e = addVectors(a, b);Pros
- Gives declaration writers flexibility to access overloads in situationally appropriate ways
- Good type safety and overload support
Cons
- Requires a great number of helper types to cover all of the operators
- Need to ensure declared functions are only used for calls and cannot be assigned to/from
- Obfuscates the fact that things are transpiled specially to end users
Option B: Annotations
Similar to Option A, but using annotations instead of types.
declare class Vector {
/** @__add */ add(other: Vector): Vector;
/** @__add */ static add(a: Vector, b: Vector): Vector;
}
/** @__add */ declare function addVectors(a: Vector, b: Vector): Vector;Pros
- Same as helper types: flexible and type-safe
Cons
- Like helper types, requires extra checks to ensure they aren't mis-used
- Uses annotations, which we've been trying to move away from
Option C: Builtin Helper Function
Provide simple built-in helpers to access operators (similar to $multi).
declare function $add(a: any, b: any): any;
// or
declare function $add<T>(a: T, b: T): T;
declare function $add<A, B>(a: A, b: B): A;Pros
- No need for explicit support from declarations
- Makes it clear to users they are invoking something special
Cons
- Limited type-safety. Either we use the
anyversion, or prevent support for possible type combinations with the generic version.
Option D: User-Defined Helper Function
Allow declaration writers to supply overloads of the helper functions to map types.
// Must be defined by declaration writers
declare function $add(a: Vector, b: Vector): Vector;Pros
- Since declaring global functions with
$names is not currently allowed, there's no chance of collisions with existing code - Provides type safety and overload capabilities that Option C did not
Cons
- Potential collision issues with different declarations defining similar overloads.
- If two declarations have identical types for inputs, but different return types, there will be problems.
My preference right now is for option A. But I'd like to see if there are other arguments for/against other options, or if there are other possibilities I'm not thinking of.