X Tutup
Skip to content

Overloaded Operator Mapping Proposals #964

@tomblind

Description

@tomblind

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 any version, 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    discussionOpen for discussion and ideas

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      X Tutup