From 10fedce5bcccbc619e2d95771ec2327bda201b18 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 22 May 2020 21:29:28 +0000 Subject: [PATCH 1/2] Fix class .toString inheritance --- CHANGELOG.md | 3 +++ src/lualib/ClassExtends.ts | 1 + src/lualib/declarations/tstl.d.ts | 1 + test/unit/classes/classes.spec.ts | 26 ++++++++++++++++++++------ 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e2cc02ee..96c8cd251 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - TypeScript has been updated to 3.9. See [release notes](https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/) for details. This update includes some fixes specific to our API usage: + - Importing a non-module using `import "./file"` produced a TS2307 error [#35973](https://github.com/microsoft/TypeScript/issues/35973) - TypeScript now tries to find a call signature even in presence of type errors (#36665)(https://github.com/microsoft/TypeScript/pull/36665): ```ts @@ -18,6 +19,8 @@ foo(1) ``` +- Fixed extending a class not keeping `toString` implementation from a super class + ## 0.33.0 - Added support for nullish coalescing `A ?? B`. diff --git a/src/lualib/ClassExtends.ts b/src/lualib/ClassExtends.ts index e297cba93..0df851629 100644 --- a/src/lualib/ClassExtends.ts +++ b/src/lualib/ClassExtends.ts @@ -16,4 +16,5 @@ function __TS__ClassExtends(this: void, target: LuaClass, base: LuaClass): void // Re-add metatable events defined by accessors with `__TS__SetDescriptor` if (typeof base.prototype.__index === "function") target.prototype.__index = base.prototype.__index; if (typeof base.prototype.__newindex === "function") target.prototype.__newindex = base.prototype.__newindex; + if (typeof base.prototype.__tostring === "function") target.prototype.__tostring = base.prototype.__tostring; } diff --git a/src/lualib/declarations/tstl.d.ts b/src/lualib/declarations/tstl.d.ts index a983ee66d..57e436ae3 100644 --- a/src/lualib/declarations/tstl.d.ts +++ b/src/lualib/declarations/tstl.d.ts @@ -10,6 +10,7 @@ interface Metatable { _descriptors?: Record; __index?: any; __newindex?: any; + __tostring?: any; } interface LuaClass extends Metatable { diff --git a/test/unit/classes/classes.spec.ts b/test/unit/classes/classes.spec.ts index 52bcdd0fc..71f616e83 100644 --- a/test/unit/classes/classes.spec.ts +++ b/test/unit/classes/classes.spec.ts @@ -352,15 +352,29 @@ test("ClassComputedMethodCall", () => { `.expectToMatchJsResult(); }); -test("ClassToString", () => { +test(".toString()", () => { util.testFunction` - class a { - public toString(): string { - return "instance of a"; + class A { + public toString() { + return "A"; } } - let inst = new a(); - return inst.toString(); + + return new A().toString(); + `.expectToMatchJsResult(); +}); + +test(".toString() with inheritance", () => { + util.testFunction` + class A { + public toString() { + return "A"; + } + } + + class B extends A {} + + return new B().toString(); `.expectToMatchJsResult(); }); From 7fa61c7c681c5e45145e7255360456bdbadb2f21 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sat, 23 May 2020 16:33:32 +0000 Subject: [PATCH 2/2] Move tests to builtins/object and add few more toString tests --- test/unit/builtins/object.spec.ts | 93 +++++++++++++++++++++++++++++++ test/unit/classes/classes.spec.ts | 50 ----------------- 2 files changed, 93 insertions(+), 50 deletions(-) diff --git a/test/unit/builtins/object.spec.ts b/test/unit/builtins/object.spec.ts index 067da81f6..b4866d2e7 100644 --- a/test/unit/builtins/object.spec.ts +++ b/test/unit/builtins/object.spec.ts @@ -28,3 +28,96 @@ test.each(["[]", '[["a", 1], ["b", 2]]', '[["a", 1], ["a", 2]]', 'new Map([["foo util.testExpression`Object.fromEntries(${entries})`.expectToMatchJsResult(); } ); + +describe(".toString()", () => { + const toStringDeclaration = ` + function toString(value: object) { + const result = value.toString(); + return result === "[object Object]" || result.startsWith("table: ") ? "table" : result; + } + `; + + test("class override", () => { + util.testFunction` + ${toStringDeclaration} + class A { + public toString() { + return "A"; + } + } + + return toString(new A()); + `.expectToMatchJsResult(); + }); + + test("inherited class override", () => { + util.testFunction` + ${toStringDeclaration} + class A { + public toString() { + return "A"; + } + } + + class B extends A {} + + return { A: toString(new A()), B: toString(new B()) }; + `.expectToMatchJsResult(); + }); + + test("don't affect inherited class", () => { + util.testFunction` + ${toStringDeclaration} + class A {} + + class B extends A { + public toString() { + return "B"; + } + } + + return { A: toString(new A()), B: toString(new B()) }; + `.expectToMatchJsResult(); + }); + + test("override inherited class override", () => { + util.testFunction` + ${toStringDeclaration} + class A { + public toString() { + return "A"; + } + } + + class B extends A { + public toString() { + return "B"; + } + } + + return { A: toString(new A()), B: toString(new B()) }; + `.expectToMatchJsResult(); + }); +}); + +describe(".hasOwnProperty()", () => { + test("class field", () => { + util.testFunction` + class A { + public field = true; + } + + return new A().hasOwnProperty("field"); + `.expectToMatchJsResult(); + }); + + test("class method", () => { + util.testFunction` + class A { + public method() {} + } + + return new A().hasOwnProperty("method"); + `.expectToMatchJsResult(); + }); +}); diff --git a/test/unit/classes/classes.spec.ts b/test/unit/classes/classes.spec.ts index 71f616e83..f4c14df34 100644 --- a/test/unit/classes/classes.spec.ts +++ b/test/unit/classes/classes.spec.ts @@ -352,56 +352,6 @@ test("ClassComputedMethodCall", () => { `.expectToMatchJsResult(); }); -test(".toString()", () => { - util.testFunction` - class A { - public toString() { - return "A"; - } - } - - return new A().toString(); - `.expectToMatchJsResult(); -}); - -test(".toString() with inheritance", () => { - util.testFunction` - class A { - public toString() { - return "A"; - } - } - - class B extends A {} - - return new B().toString(); - `.expectToMatchJsResult(); -}); - -test("HasOwnProperty true", () => { - util.testFunction` - class a { - public test(): void { - } - } - let inst = new a(); - inst["prop"] = 17; - return inst.hasOwnProperty("prop"); - `.expectToMatchJsResult(); -}); - -test("HasOwnProperty false", () => { - util.testFunction` - class a { - public test(): void { - } - } - let inst = new a(); - inst["prop"] = 17; - return inst.hasOwnProperty("test"); - `.expectToMatchJsResult(); -}); - test("CastClassMethodCall", () => { util.testFunction` interface result