From 7d1e24ea7d4d54029361a33826f228f3473c4f2c Mon Sep 17 00:00:00 2001 From: Lucas Azzola Date: Mon, 1 May 2017 10:41:19 +1000 Subject: [PATCH] Add supertype tests and add TSAbstractClassProperty (#1467) * feat(typescript): add supertype tests and add TSAbstractClassProperty * chore(typescript): bump parser version * fix(flow): allow both variance and accessibility mods --- package.json | 4 +- src/printer.js | 27 +- .../classes/__snapshots__/jsfmt.spec.js.snap | 735 ++++++++++++++++++ .../__snapshots__/jsfmt.spec.js.snap | 11 + .../__snapshots__/jsfmt.spec.js.snap | 16 +- .../classAbstractProperties.ts | 9 +- .../__snapshots__/jsfmt.spec.js.snap | 145 ++++ .../classAppearsToHaveMembersOfObject.ts | 7 + .../classExtendingClass.ts | 31 + .../classExtendsItselfIndirectly.ts | 11 + .../classIsSubtypeOfBaseType.ts | 7 + .../classHeritageSpecification/jsfmt.spec.js | 1 + .../classDeclarations/classInsideBlock.ts | 3 + .../classes/classDeclarations/jsfmt.spec.js | 1 + .../conformance/classes/classExpression.ts | 10 + .../conformance/classes/jsfmt.spec.js | 1 + .../classes/mixinAccessModifiers.ts | 108 +++ .../classes/mixinClassesAnnotated.ts | 60 ++ .../classes/mixinClassesAnonymous.ts | 64 ++ .../classes/mixinClassesMembers.ts | 99 +++ .../classes/nestedClassDeclaration.ts | 10 + .../thisType/__snapshots__/jsfmt.spec.js.snap | 2 +- .../__snapshots__/jsfmt.spec.js.snap | 4 +- .../abstract/__snapshots__/jsfmt.spec.js.snap | 31 + .../custom/abstract/abstractProperties.ts | 13 + 25 files changed, 1386 insertions(+), 24 deletions(-) create mode 100644 tests/typescript/conformance/classes/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/typescript/conformance/classes/classDeclarations/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classAppearsToHaveMembersOfObject.ts create mode 100644 tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classExtendingClass.ts create mode 100644 tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classExtendsItselfIndirectly.ts create mode 100644 tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classIsSubtypeOfBaseType.ts create mode 100644 tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/jsfmt.spec.js create mode 100644 tests/typescript/conformance/classes/classDeclarations/classInsideBlock.ts create mode 100644 tests/typescript/conformance/classes/classDeclarations/jsfmt.spec.js create mode 100644 tests/typescript/conformance/classes/classExpression.ts create mode 100644 tests/typescript/conformance/classes/jsfmt.spec.js create mode 100644 tests/typescript/conformance/classes/mixinAccessModifiers.ts create mode 100644 tests/typescript/conformance/classes/mixinClassesAnnotated.ts create mode 100644 tests/typescript/conformance/classes/mixinClassesAnonymous.ts create mode 100644 tests/typescript/conformance/classes/mixinClassesMembers.ts create mode 100644 tests/typescript/conformance/classes/nestedClassDeclaration.ts create mode 100644 tests/typescript/custom/abstract/abstractProperties.ts diff --git a/package.json b/package.json index db2b3bc2..13818d52 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,8 @@ "rollup-plugin-node-builtins": "2.0.0", "rollup-plugin-node-globals": "1.1.0", "rollup-plugin-node-resolve": "2.0.0", - "typescript": "2.2.1", - "typescript-eslint-parser": "git://github.com/eslint/typescript-eslint-parser.git#bfb1506c48b625871ffeb67dbec7941d460f8941" + "typescript": "2.3.2", + "typescript-eslint-parser": "git://github.com/eslint/typescript-eslint-parser.git#a294afa8158c9c088521eed72b6745eed302361c" }, "scripts": { "test": "jest", diff --git a/src/printer.js b/src/printer.js index 2740613b..1800441e 100644 --- a/src/printer.js +++ b/src/printer.js @@ -1447,30 +1447,22 @@ function genericPrintNoParens(path, options, print, args) { return concat(parts); case "ClassProperty": + case "TSAbstractClassProperty": if (n.static) parts.push("static "); - var key; + var variance = getFlowVariance(n, options); + if (variance) parts.push(variance); + + if (n.accessibility) parts.push(n.accessibility + " "); + + if (n.type === "TSAbstractClassProperty") parts.push("abstract "); if (n.computed) { - key = concat(["[", path.call(print, "key"), "]"]); + parts.push("[", path.call(print, "key"), "]"); } else { - key = printPropertyKey(path, options, print); - - var variance = getFlowVariance(n, options); - - if (variance) { - key = concat([variance, key]); - } else if (n.accessibility === "public") { - key = concat(["public ", key]); - } else if (n.accessibility === "protected") { - key = concat(["protected ", key]); - } else if (n.accessibility === "private") { - key = concat(["private ", key]); - } + parts.push(printPropertyKey(path, options, print)); } - parts.push(key); - if (n.typeAnnotation) parts.push(": ", path.call(print, "typeAnnotation")); if (n.value) parts.push(" = ", path.call(print, "value")); @@ -3598,6 +3590,7 @@ function classChildNeedsASIProtection(node) { switch (node.type) { case "ClassProperty": + case "TSAbstractClassProperty": return node.computed; // flow case "MethodDefinition": diff --git a/tests/typescript/conformance/classes/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/classes/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..b11d184e --- /dev/null +++ b/tests/typescript/conformance/classes/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,735 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`classExpression.ts 1`] = ` +var x = class C { +} + +var y = { + foo: class C2 { + } +} + +var z = class C4 { +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x = class C {}; + +var y = { + foo: class C2 {} +}; + +var z = class C4 {}; + +`; + +exports[`mixinAccessModifiers.ts 1`] = ` +// @declaration: true + +type Constructable = new (...args: any[]) => object; + +class Private { + constructor (...args: any[]) {} + private p: string; +} + +class Private2 { + constructor (...args: any[]) {} + private p: string; +} + +class Protected { + constructor (...args: any[]) {} + protected p: string; + protected static s: string; +} + +class Protected2 { + constructor (...args: any[]) {} + protected p: string; + protected static s: string; +} + +class Public { + constructor (...args: any[]) {} + public p: string; + public static s: string; +} + +class Public2 { + constructor (...args: any[]) {} + public p: string; + public static s: string; +} + +function f1(x: Private & Private2) { + x.p; // Error, private constituent makes property inaccessible +} + +function f2(x: Private & Protected) { + x.p; // Error, private constituent makes property inaccessible +} + +function f3(x: Private & Public) { + x.p; // Error, private constituent makes property inaccessible +} + +function f4(x: Protected & Protected2) { + x.p; // Error, protected when all constituents are protected +} + +function f5(x: Protected & Public) { + x.p; // Ok, public if any constituent is public +} + +function f6(x: Public & Public2) { + x.p; // Ok, public if any constituent is public +} + +declare function Mix(c1: T, c2: U): T & U; + +// Can't derive from type with inaccessible properties + +class C1 extends Mix(Private, Private2) {} +class C2 extends Mix(Private, Protected) {} +class C3 extends Mix(Private, Public) {} + +class C4 extends Mix(Protected, Protected2) { + f(c4: C4, c5: C5, c6: C6) { + c4.p; + c5.p; + c6.p; + } + static g() { + C4.s; + C5.s; + C6.s + } +} + +class C5 extends Mix(Protected, Public) { + f(c4: C4, c5: C5, c6: C6) { + c4.p; // Error, not in class deriving from Protected2 + c5.p; + c6.p; + } + static g() { + C4.s; // Error, not in class deriving from Protected2 + C5.s; + C6.s + } +} + +class C6 extends Mix(Public, Public2) { + f(c4: C4, c5: C5, c6: C6) { + c4.p; // Error, not in class deriving from Protected2 + c5.p; + c6.p; + } + static g() { + C4.s; // Error, not in class deriving from Protected2 + C5.s; + C6.s + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @declaration: true + +type Constructable = new(...args: any[]) => object; + +class Private { + constructor(...args: any[]) {} + private p: string; +} + +class Private2 { + constructor(...args: any[]) {} + private p: string; +} + +class Protected { + constructor(...args: any[]) {} + protected p: string; + static protected s: string; +} + +class Protected2 { + constructor(...args: any[]) {} + protected p: string; + static protected s: string; +} + +class Public { + constructor(...args: any[]) {} + public p: string; + static public s: string; +} + +class Public2 { + constructor(...args: any[]) {} + public p: string; + static public s: string; +} + +function f1(x: Private & Private2) { + x.p; // Error, private constituent makes property inaccessible +} + +function f2(x: Private & Protected) { + x.p; // Error, private constituent makes property inaccessible +} + +function f3(x: Private & Public) { + x.p; // Error, private constituent makes property inaccessible +} + +function f4(x: Protected & Protected2) { + x.p; // Error, protected when all constituents are protected +} + +function f5(x: Protected & Public) { + x.p; // Ok, public if any constituent is public +} + +function f6(x: Public & Public2) { + x.p; // Ok, public if any constituent is public +} + +declare function Mix(c1: T, c2: U): T & U + +// Can't derive from type with inaccessible properties + +class C1 extends Mix(Private, Private2) {} +class C2 extends Mix(Private, Protected) {} +class C3 extends Mix(Private, Public) {} + +class C4 extends Mix(Protected, Protected2) { + f(c4: C4, c5: C5, c6: C6) { + c4.p; + c5.p; + c6.p; + } + static g() { + C4.s; + C5.s; + C6.s; + } +} + +class C5 extends Mix(Protected, Public) { + f(c4: C4, c5: C5, c6: C6) { + c4.p; // Error, not in class deriving from Protected2 + c5.p; + c6.p; + } + static g() { + C4.s; // Error, not in class deriving from Protected2 + C5.s; + C6.s; + } +} + +class C6 extends Mix(Public, Public2) { + f(c4: C4, c5: C5, c6: C6) { + c4.p; // Error, not in class deriving from Protected2 + c5.p; + c6.p; + } + static g() { + C4.s; // Error, not in class deriving from Protected2 + C5.s; + C6.s; + } +} + +`; + +exports[`mixinClassesAnnotated.ts 1`] = ` +// @declaration: true + +type Constructor = new(...args: any[]) => T; + +class Base { + constructor(public x: number, public y: number) {} +} + +class Derived extends Base { + constructor(x: number, y: number, public z: number) { + super(x, y); + } +} + +const Printable = >(superClass: T): Constructor & { message: string } & T => + class extends superClass { + static message = "hello"; + print() { + const output = this.x + "," + this.y; + } + } + + +function Tagged>(superClass: T): Constructor & T { + class C extends superClass { + _tag: string; + constructor(...args: any[]) { + super(...args); + this._tag = "hello"; + } + } + return C; +} + +const Thing1 = Tagged(Derived); +const Thing2 = Tagged(Printable(Derived)); +Thing2.message; + +function f1() { + const thing = new Thing1(1, 2, 3); + thing.x; + thing._tag; +} + +function f2() { + const thing = new Thing2(1, 2, 3); + thing.x; + thing._tag; + thing.print(); +} + +class Thing3 extends Thing2 { + constructor(tag: string) { + super(10, 20, 30); + this._tag = tag; + } + test() { + this.print(); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @declaration: true + +type Constructor = new(...args: any[]) => T; + +class Base { + constructor(public x: number, public y: number) {} +} + +class Derived extends Base { + constructor(x: number, y: number, public z: number) { + super(x, y); + } +} + +const Printable = >( + superClass: T +): Constructor & { message: string } & T => class + extends superClass { + static message = "hello"; + print() { + const output = this.x + "," + this.y; + } +}; + +function Tagged>( + superClass: T +): Constructor & T { + class C extends superClass { + _tag: string; + constructor(...args: any[]) { + super(...args); + this._tag = "hello"; + } + } + return C; +} + +const Thing1 = Tagged(Derived); +const Thing2 = Tagged(Printable(Derived)); +Thing2.message; + +function f1() { + const thing = new Thing1(1, 2, 3); + thing.x; + thing._tag; +} + +function f2() { + const thing = new Thing2(1, 2, 3); + thing.x; + thing._tag; + thing.print(); +} + +class Thing3 extends Thing2 { + constructor(tag: string) { + super(10, 20, 30); + this._tag = tag; + } + test() { + this.print(); + } +} + +`; + +exports[`mixinClassesAnonymous.ts 1`] = ` +type Constructor = new(...args: any[]) => T; + +class Base { + constructor(public x: number, public y: number) {} +} + +class Derived extends Base { + constructor(x: number, y: number, public z: number) { + super(x, y); + } +} + +const Printable = >(superClass: T) => class extends superClass { + static message = "hello"; + print() { + const output = this.x + "," + this.y; + } +} + +function Tagged>(superClass: T) { + class C extends superClass { + _tag: string; + constructor(...args: any[]) { + super(...args); + this._tag = "hello"; + } + } + return C; +} + +const Thing1 = Tagged(Derived); +const Thing2 = Tagged(Printable(Derived)); +Thing2.message; + +function f1() { + const thing = new Thing1(1, 2, 3); + thing.x; + thing._tag; +} + +function f2() { + const thing = new Thing2(1, 2, 3); + thing.x; + thing._tag; + thing.print(); +} + +class Thing3 extends Thing2 { + constructor(tag: string) { + super(10, 20, 30); + this._tag = tag; + } + test() { + this.print(); + } +} + +// Repro from #13805 + +const Timestamped = >(Base: CT) => { + return class extends Base { + timestamp = new Date(); + }; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +type Constructor = new(...args: any[]) => T; + +class Base { + constructor(public x: number, public y: number) {} +} + +class Derived extends Base { + constructor(x: number, y: number, public z: number) { + super(x, y); + } +} + +const Printable = >(superClass: T) => class + extends superClass { + static message = "hello"; + print() { + const output = this.x + "," + this.y; + } +}; + +function Tagged>(superClass: T) { + class C extends superClass { + _tag: string; + constructor(...args: any[]) { + super(...args); + this._tag = "hello"; + } + } + return C; +} + +const Thing1 = Tagged(Derived); +const Thing2 = Tagged(Printable(Derived)); +Thing2.message; + +function f1() { + const thing = new Thing1(1, 2, 3); + thing.x; + thing._tag; +} + +function f2() { + const thing = new Thing2(1, 2, 3); + thing.x; + thing._tag; + thing.print(); +} + +class Thing3 extends Thing2 { + constructor(tag: string) { + super(10, 20, 30); + this._tag = tag; + } + test() { + this.print(); + } +} + +// Repro from #13805 + +const Timestamped = >(Base: CT) => { + return class extends Base { + timestamp = new Date(); + }; +}; + +`; + +exports[`mixinClassesMembers.ts 1`] = ` +// @declaration: true + +declare class C1 { + public a: number; + protected b: number; + private c: number; + constructor(s: string); + constructor(n: number); +} + +declare class M1 { + constructor(...args: any[]); + p: number; + static p: number; +} + +declare class M2 { + constructor(...args: any[]); + f(): number; + static f(): number; +} + +declare const Mixed1: typeof M1 & typeof C1; +declare const Mixed2: typeof C1 & typeof M1; +declare const Mixed3: typeof M2 & typeof M1 & typeof C1; +declare const Mixed4: typeof C1 & typeof M1 & typeof M2; +declare const Mixed5: typeof M1 & typeof M2; + +function f1() { + let x1 = new Mixed1("hello"); + let x2 = new Mixed1(42); + let x3 = new Mixed2("hello"); + let x4 = new Mixed2(42); + let x5 = new Mixed3("hello"); + let x6 = new Mixed3(42); + let x7 = new Mixed4("hello"); + let x8 = new Mixed4(42); + let x9 = new Mixed5(); +} + +function f2() { + let x = new Mixed1("hello"); + x.a; + x.p; + Mixed1.p; +} + +function f3() { + let x = new Mixed2("hello"); + x.a; + x.p; + Mixed2.p; +} + +function f4() { + let x = new Mixed3("hello"); + x.a; + x.p; + x.f(); + Mixed3.p; + Mixed3.f(); +} + +function f5() { + let x = new Mixed4("hello"); + x.a; + x.p; + x.f(); + Mixed4.p; + Mixed4.f(); +} + +function f6() { + let x = new Mixed5(); + x.p; + x.f(); + Mixed5.p; + Mixed5.f(); +} + +class C2 extends Mixed1 { + constructor() { + super("hello"); + this.a; + this.b; + this.p; + } +} + +class C3 extends Mixed3 { + constructor() { + super(42); + this.a; + this.b; + this.p; + this.f(); + } + f() { return super.f(); } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @declaration: true + +declare class C1 { + public a: number; + protected b: number; + private c: number; + constructor(s: string); + constructor(n: number); +} + +declare class M1 { + constructor(...args: any[]); + p: number; + static p: number; +} + +declare class M2 { + constructor(...args: any[]); + f(): number; + static f(): number; +} + +declare const Mixed1: typeof M1 & typeof C1; +declare const Mixed2: typeof C1 & typeof M1; +declare const Mixed3: typeof M2 & typeof M1 & typeof C1; +declare const Mixed4: typeof C1 & typeof M1 & typeof M2; +declare const Mixed5: typeof M1 & typeof M2; + +function f1() { + let x1 = new Mixed1("hello"); + let x2 = new Mixed1(42); + let x3 = new Mixed2("hello"); + let x4 = new Mixed2(42); + let x5 = new Mixed3("hello"); + let x6 = new Mixed3(42); + let x7 = new Mixed4("hello"); + let x8 = new Mixed4(42); + let x9 = new Mixed5(); +} + +function f2() { + let x = new Mixed1("hello"); + x.a; + x.p; + Mixed1.p; +} + +function f3() { + let x = new Mixed2("hello"); + x.a; + x.p; + Mixed2.p; +} + +function f4() { + let x = new Mixed3("hello"); + x.a; + x.p; + x.f(); + Mixed3.p; + Mixed3.f(); +} + +function f5() { + let x = new Mixed4("hello"); + x.a; + x.p; + x.f(); + Mixed4.p; + Mixed4.f(); +} + +function f6() { + let x = new Mixed5(); + x.p; + x.f(); + Mixed5.p; + Mixed5.f(); +} + +class C2 extends Mixed1 { + constructor() { + super("hello"); + this.a; + this.b; + this.p; + } +} + +class C3 extends Mixed3 { + constructor() { + super(42); + this.a; + this.b; + this.p; + this.f(); + } + f() { + return super.f(); + } +} + +`; + +exports[`nestedClassDeclaration.ts 1`] = ` +// nested classes are not allowed + +class C { + x: string; +} + +function foo() { + class C3 { + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// nested classes are not allowed + +class C { + x: string; +} + +function foo() { + class C3 {} +} + +`; diff --git a/tests/typescript/conformance/classes/classDeclarations/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/classes/classDeclarations/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..e1d1cb4c --- /dev/null +++ b/tests/typescript/conformance/classes/classDeclarations/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`classInsideBlock.ts 1`] = ` +function foo() { + class C { } +}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function foo() { + class C {} +} + +`; diff --git a/tests/typescript/conformance/classes/classDeclarations/classAbstractKeyword/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/classes/classDeclarations/classAbstractKeyword/__snapshots__/jsfmt.spec.js.snap index 3c46f2bb..959001ef 100644 --- a/tests/typescript/conformance/classes/classDeclarations/classAbstractKeyword/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript/conformance/classes/classDeclarations/classAbstractKeyword/__snapshots__/jsfmt.spec.js.snap @@ -658,13 +658,27 @@ class DD extends BB { `; exports[`classAbstractProperties.ts 1`] = ` -abstract class A { +abstract class A { + abstract x : number; + public abstract y : number; + protected abstract z : number; + private abstract w : number; + + abstract m: () => void; + abstract foo_x() : number; public abstract foo_y() : number; protected abstract foo_z() : number; private abstract foo_w() : number; }~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ abstract class A { + abstract x: number; + public abstract y: number; + protected abstract z: number; + private abstract w: number; + + abstract m: () => void; + abstract foo_x(): number; public abstract foo_y(): number; protected abstract foo_z(): number; diff --git a/tests/typescript/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractProperties.ts b/tests/typescript/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractProperties.ts index 775f4c31..e6dc355a 100644 --- a/tests/typescript/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractProperties.ts +++ b/tests/typescript/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractProperties.ts @@ -1,4 +1,11 @@ -abstract class A { +abstract class A { + abstract x : number; + public abstract y : number; + protected abstract z : number; + private abstract w : number; + + abstract m: () => void; + abstract foo_x() : number; public abstract foo_y() : number; protected abstract foo_z() : number; diff --git a/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..3fa57947 --- /dev/null +++ b/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,145 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`classAppearsToHaveMembersOfObject.ts 1`] = ` +class C { foo: string; } + +var c: C; +var r = c.toString(); +var r2 = c.hasOwnProperty(''); +var o: Object = c; +var o2: {} = c; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class C { + foo: string; +} + +var c: C; +var r = c.toString(); +var r2 = c.hasOwnProperty(""); +var o: Object = c; +var o2: {} = c; + +`; + +exports[`classExtendingClass.ts 1`] = ` +class C { + foo: string; + thing() { } + static other() { } +} + +class D extends C { + bar: string; +} + +var d: D; +var r = d.foo; +var r2 = d.bar; +var r3 = d.thing(); +var r4 = D.other(); + +class C2 { + foo: T; + thing(x: T) { } + static other(x: T) { } +} + +class D2 extends C2 { + bar: string; +} + +var d2: D2; +var r5 = d2.foo; +var r6 = d2.bar; +var r7 = d2.thing(''); +var r8 = D2.other(1);~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class C { + foo: string; + thing() {} + static other() {} +} + +class D extends C { + bar: string; +} + +var d: D; +var r = d.foo; +var r2 = d.bar; +var r3 = d.thing(); +var r4 = D.other(); + +class C2 { + foo: T; + thing(x: T) {} + static other(x: T) {} +} + +class D2 extends C2 { + bar: string; +} + +var d2: D2; +var r5 = d2.foo; +var r6 = d2.bar; +var r7 = d2.thing(""); +var r8 = D2.other(1); + +`; + +exports[`classExtendsItselfIndirectly.ts 1`] = ` +class C extends E { foo: string; } // error + +class D extends C { bar: string; } + +class E extends D { baz: number; } + +class C2 extends E2 { foo: T; } // error + +class D2 extends C2 { bar: T; } + +class E2 extends D2 { baz: T; }~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class C extends E { + foo: string; +} // error + +class D extends C { + bar: string; +} + +class E extends D { + baz: number; +} + +class C2 extends E2 { + foo: T; +} // error + +class D2 extends C2 { + bar: T; +} + +class E2 extends D2 { + baz: T; +} + +`; + +exports[`classIsSubtypeOfBaseType.ts 1`] = ` +class Base { + foo: T; +} + +class Derived extends Base { + foo: any; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class Base { + foo: T; +} + +class Derived extends Base { + foo: any; +} + +`; diff --git a/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classAppearsToHaveMembersOfObject.ts b/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classAppearsToHaveMembersOfObject.ts new file mode 100644 index 00000000..38d3a6df --- /dev/null +++ b/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classAppearsToHaveMembersOfObject.ts @@ -0,0 +1,7 @@ +class C { foo: string; } + +var c: C; +var r = c.toString(); +var r2 = c.hasOwnProperty(''); +var o: Object = c; +var o2: {} = c; diff --git a/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classExtendingClass.ts b/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classExtendingClass.ts new file mode 100644 index 00000000..3e3faa7e --- /dev/null +++ b/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classExtendingClass.ts @@ -0,0 +1,31 @@ +class C { + foo: string; + thing() { } + static other() { } +} + +class D extends C { + bar: string; +} + +var d: D; +var r = d.foo; +var r2 = d.bar; +var r3 = d.thing(); +var r4 = D.other(); + +class C2 { + foo: T; + thing(x: T) { } + static other(x: T) { } +} + +class D2 extends C2 { + bar: string; +} + +var d2: D2; +var r5 = d2.foo; +var r6 = d2.bar; +var r7 = d2.thing(''); +var r8 = D2.other(1); \ No newline at end of file diff --git a/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classExtendsItselfIndirectly.ts b/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classExtendsItselfIndirectly.ts new file mode 100644 index 00000000..92b089c0 --- /dev/null +++ b/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classExtendsItselfIndirectly.ts @@ -0,0 +1,11 @@ +class C extends E { foo: string; } // error + +class D extends C { bar: string; } + +class E extends D { baz: number; } + +class C2 extends E2 { foo: T; } // error + +class D2 extends C2 { bar: T; } + +class E2 extends D2 { baz: T; } \ No newline at end of file diff --git a/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classIsSubtypeOfBaseType.ts b/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classIsSubtypeOfBaseType.ts new file mode 100644 index 00000000..1f88eec4 --- /dev/null +++ b/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/classIsSubtypeOfBaseType.ts @@ -0,0 +1,7 @@ +class Base { + foo: T; +} + +class Derived extends Base { + foo: any; +} diff --git a/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/jsfmt.spec.js b/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/jsfmt.spec.js new file mode 100644 index 00000000..bc085c48 --- /dev/null +++ b/tests/typescript/conformance/classes/classDeclarations/classHeritageSpecification/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "typescript" }); diff --git a/tests/typescript/conformance/classes/classDeclarations/classInsideBlock.ts b/tests/typescript/conformance/classes/classDeclarations/classInsideBlock.ts new file mode 100644 index 00000000..efc3ccff --- /dev/null +++ b/tests/typescript/conformance/classes/classDeclarations/classInsideBlock.ts @@ -0,0 +1,3 @@ +function foo() { + class C { } +} \ No newline at end of file diff --git a/tests/typescript/conformance/classes/classDeclarations/jsfmt.spec.js b/tests/typescript/conformance/classes/classDeclarations/jsfmt.spec.js new file mode 100644 index 00000000..bc085c48 --- /dev/null +++ b/tests/typescript/conformance/classes/classDeclarations/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "typescript" }); diff --git a/tests/typescript/conformance/classes/classExpression.ts b/tests/typescript/conformance/classes/classExpression.ts new file mode 100644 index 00000000..d10591bd --- /dev/null +++ b/tests/typescript/conformance/classes/classExpression.ts @@ -0,0 +1,10 @@ +var x = class C { +} + +var y = { + foo: class C2 { + } +} + +var z = class C4 { +} diff --git a/tests/typescript/conformance/classes/jsfmt.spec.js b/tests/typescript/conformance/classes/jsfmt.spec.js new file mode 100644 index 00000000..bc085c48 --- /dev/null +++ b/tests/typescript/conformance/classes/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "typescript" }); diff --git a/tests/typescript/conformance/classes/mixinAccessModifiers.ts b/tests/typescript/conformance/classes/mixinAccessModifiers.ts new file mode 100644 index 00000000..a628371e --- /dev/null +++ b/tests/typescript/conformance/classes/mixinAccessModifiers.ts @@ -0,0 +1,108 @@ +// @declaration: true + +type Constructable = new (...args: any[]) => object; + +class Private { + constructor (...args: any[]) {} + private p: string; +} + +class Private2 { + constructor (...args: any[]) {} + private p: string; +} + +class Protected { + constructor (...args: any[]) {} + protected p: string; + protected static s: string; +} + +class Protected2 { + constructor (...args: any[]) {} + protected p: string; + protected static s: string; +} + +class Public { + constructor (...args: any[]) {} + public p: string; + public static s: string; +} + +class Public2 { + constructor (...args: any[]) {} + public p: string; + public static s: string; +} + +function f1(x: Private & Private2) { + x.p; // Error, private constituent makes property inaccessible +} + +function f2(x: Private & Protected) { + x.p; // Error, private constituent makes property inaccessible +} + +function f3(x: Private & Public) { + x.p; // Error, private constituent makes property inaccessible +} + +function f4(x: Protected & Protected2) { + x.p; // Error, protected when all constituents are protected +} + +function f5(x: Protected & Public) { + x.p; // Ok, public if any constituent is public +} + +function f6(x: Public & Public2) { + x.p; // Ok, public if any constituent is public +} + +declare function Mix(c1: T, c2: U): T & U; + +// Can't derive from type with inaccessible properties + +class C1 extends Mix(Private, Private2) {} +class C2 extends Mix(Private, Protected) {} +class C3 extends Mix(Private, Public) {} + +class C4 extends Mix(Protected, Protected2) { + f(c4: C4, c5: C5, c6: C6) { + c4.p; + c5.p; + c6.p; + } + static g() { + C4.s; + C5.s; + C6.s + } +} + +class C5 extends Mix(Protected, Public) { + f(c4: C4, c5: C5, c6: C6) { + c4.p; // Error, not in class deriving from Protected2 + c5.p; + c6.p; + } + static g() { + C4.s; // Error, not in class deriving from Protected2 + C5.s; + C6.s + } +} + +class C6 extends Mix(Public, Public2) { + f(c4: C4, c5: C5, c6: C6) { + c4.p; // Error, not in class deriving from Protected2 + c5.p; + c6.p; + } + static g() { + C4.s; // Error, not in class deriving from Protected2 + C5.s; + C6.s + } +} diff --git a/tests/typescript/conformance/classes/mixinClassesAnnotated.ts b/tests/typescript/conformance/classes/mixinClassesAnnotated.ts new file mode 100644 index 00000000..9910f50a --- /dev/null +++ b/tests/typescript/conformance/classes/mixinClassesAnnotated.ts @@ -0,0 +1,60 @@ +// @declaration: true + +type Constructor = new(...args: any[]) => T; + +class Base { + constructor(public x: number, public y: number) {} +} + +class Derived extends Base { + constructor(x: number, y: number, public z: number) { + super(x, y); + } +} + +const Printable = >(superClass: T): Constructor & { message: string } & T => + class extends superClass { + static message = "hello"; + print() { + const output = this.x + "," + this.y; + } + } + + +function Tagged>(superClass: T): Constructor & T { + class C extends superClass { + _tag: string; + constructor(...args: any[]) { + super(...args); + this._tag = "hello"; + } + } + return C; +} + +const Thing1 = Tagged(Derived); +const Thing2 = Tagged(Printable(Derived)); +Thing2.message; + +function f1() { + const thing = new Thing1(1, 2, 3); + thing.x; + thing._tag; +} + +function f2() { + const thing = new Thing2(1, 2, 3); + thing.x; + thing._tag; + thing.print(); +} + +class Thing3 extends Thing2 { + constructor(tag: string) { + super(10, 20, 30); + this._tag = tag; + } + test() { + this.print(); + } +} diff --git a/tests/typescript/conformance/classes/mixinClassesAnonymous.ts b/tests/typescript/conformance/classes/mixinClassesAnonymous.ts new file mode 100644 index 00000000..0e422888 --- /dev/null +++ b/tests/typescript/conformance/classes/mixinClassesAnonymous.ts @@ -0,0 +1,64 @@ +type Constructor = new(...args: any[]) => T; + +class Base { + constructor(public x: number, public y: number) {} +} + +class Derived extends Base { + constructor(x: number, y: number, public z: number) { + super(x, y); + } +} + +const Printable = >(superClass: T) => class extends superClass { + static message = "hello"; + print() { + const output = this.x + "," + this.y; + } +} + +function Tagged>(superClass: T) { + class C extends superClass { + _tag: string; + constructor(...args: any[]) { + super(...args); + this._tag = "hello"; + } + } + return C; +} + +const Thing1 = Tagged(Derived); +const Thing2 = Tagged(Printable(Derived)); +Thing2.message; + +function f1() { + const thing = new Thing1(1, 2, 3); + thing.x; + thing._tag; +} + +function f2() { + const thing = new Thing2(1, 2, 3); + thing.x; + thing._tag; + thing.print(); +} + +class Thing3 extends Thing2 { + constructor(tag: string) { + super(10, 20, 30); + this._tag = tag; + } + test() { + this.print(); + } +} + +// Repro from #13805 + +const Timestamped = >(Base: CT) => { + return class extends Base { + timestamp = new Date(); + }; +} diff --git a/tests/typescript/conformance/classes/mixinClassesMembers.ts b/tests/typescript/conformance/classes/mixinClassesMembers.ts new file mode 100644 index 00000000..905518c8 --- /dev/null +++ b/tests/typescript/conformance/classes/mixinClassesMembers.ts @@ -0,0 +1,99 @@ +// @declaration: true + +declare class C1 { + public a: number; + protected b: number; + private c: number; + constructor(s: string); + constructor(n: number); +} + +declare class M1 { + constructor(...args: any[]); + p: number; + static p: number; +} + +declare class M2 { + constructor(...args: any[]); + f(): number; + static f(): number; +} + +declare const Mixed1: typeof M1 & typeof C1; +declare const Mixed2: typeof C1 & typeof M1; +declare const Mixed3: typeof M2 & typeof M1 & typeof C1; +declare const Mixed4: typeof C1 & typeof M1 & typeof M2; +declare const Mixed5: typeof M1 & typeof M2; + +function f1() { + let x1 = new Mixed1("hello"); + let x2 = new Mixed1(42); + let x3 = new Mixed2("hello"); + let x4 = new Mixed2(42); + let x5 = new Mixed3("hello"); + let x6 = new Mixed3(42); + let x7 = new Mixed4("hello"); + let x8 = new Mixed4(42); + let x9 = new Mixed5(); +} + +function f2() { + let x = new Mixed1("hello"); + x.a; + x.p; + Mixed1.p; +} + +function f3() { + let x = new Mixed2("hello"); + x.a; + x.p; + Mixed2.p; +} + +function f4() { + let x = new Mixed3("hello"); + x.a; + x.p; + x.f(); + Mixed3.p; + Mixed3.f(); +} + +function f5() { + let x = new Mixed4("hello"); + x.a; + x.p; + x.f(); + Mixed4.p; + Mixed4.f(); +} + +function f6() { + let x = new Mixed5(); + x.p; + x.f(); + Mixed5.p; + Mixed5.f(); +} + +class C2 extends Mixed1 { + constructor() { + super("hello"); + this.a; + this.b; + this.p; + } +} + +class C3 extends Mixed3 { + constructor() { + super(42); + this.a; + this.b; + this.p; + this.f(); + } + f() { return super.f(); } +} diff --git a/tests/typescript/conformance/classes/nestedClassDeclaration.ts b/tests/typescript/conformance/classes/nestedClassDeclaration.ts new file mode 100644 index 00000000..a6907f84 --- /dev/null +++ b/tests/typescript/conformance/classes/nestedClassDeclaration.ts @@ -0,0 +1,10 @@ +// nested classes are not allowed + +class C { + x: string; +} + +function foo() { + class C3 { + } +} diff --git a/tests/typescript/conformance/types/thisType/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/types/thisType/__snapshots__/jsfmt.spec.js.snap index 4a037b5d..5bffd5ef 100644 --- a/tests/typescript/conformance/types/thisType/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript/conformance/types/thisType/__snapshots__/jsfmt.spec.js.snap @@ -5,7 +5,7 @@ declare class MyArray extends Array { sort(compareFn?: (a: T, b: T) => number): this; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -declare class MyArray extends Array { +declare class MyArray extends Array { sort(compareFn?: (a: T, b: T) => number): this; } diff --git a/tests/typescript/conformance/types/typeParameters/typeParameterLists/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/types/typeParameters/typeParameterLists/__snapshots__/jsfmt.spec.js.snap index 8b0ed082..4d6ebd05 100644 --- a/tests/typescript/conformance/types/typeParameters/typeParameterLists/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript/conformance/types/typeParameters/typeParameterLists/__snapshots__/jsfmt.spec.js.snap @@ -104,7 +104,7 @@ class C2 { // no errors expected class C { - g() { + g() { var x: T; x.toFixed(); } @@ -116,7 +116,7 @@ class C { } class C2 { - g() { + g() { var x: U; x.toFixed(); } diff --git a/tests/typescript/custom/abstract/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/custom/abstract/__snapshots__/jsfmt.spec.js.snap index b67ac0c2..8b7d1e6a 100644 --- a/tests/typescript/custom/abstract/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript/custom/abstract/__snapshots__/jsfmt.spec.js.snap @@ -30,3 +30,34 @@ let abstract; export class Y {} `; + +exports[`abstractProperties.ts 1`] = ` +abstract class Foo { + abstract private a: 1; + private abstract b: 2; + static abstract c: 3; + + abstract private d = 4; + private abstract e = 5; + static abstract f = 6; + + abstract private ['g'] = 4; + private abstract ['h'] = 5; + static abstract ['i'] = 6; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +abstract class Foo { + private abstract a: 1; + private abstract b: 2; + static abstract c: 3; + + private abstract d = 4; + private abstract e = 5; + static abstract f = 6; + + private abstract ["g"] = 4; + private abstract ["h"] = 5; + static abstract ["i"] = 6; +} + +`; diff --git a/tests/typescript/custom/abstract/abstractProperties.ts b/tests/typescript/custom/abstract/abstractProperties.ts new file mode 100644 index 00000000..a533012f --- /dev/null +++ b/tests/typescript/custom/abstract/abstractProperties.ts @@ -0,0 +1,13 @@ +abstract class Foo { + abstract private a: 1; + private abstract b: 2; + static abstract c: 3; + + abstract private d = 4; + private abstract e = 5; + static abstract f = 6; + + abstract private ['g'] = 4; + private abstract ['h'] = 5; + static abstract ['i'] = 6; +}