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 modsmaster
parent
3161bd0787
commit
7d1e24ea7d
|
@ -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",
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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<T, U>(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<T, U>(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<T> = 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 = <T extends Constructor<Base>>(superClass: T): Constructor<Printable> & { message: string } & T =>
|
||||
class extends superClass {
|
||||
static message = "hello";
|
||||
print() {
|
||||
const output = this.x + "," + this.y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function Tagged<T extends Constructor<{}>>(superClass: T): Constructor<Tagged> & 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<T> = 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 = <T extends Constructor<Base>>(
|
||||
superClass: T
|
||||
): Constructor<Printable> & { message: string } & T => class
|
||||
extends superClass {
|
||||
static message = "hello";
|
||||
print() {
|
||||
const output = this.x + "," + this.y;
|
||||
}
|
||||
};
|
||||
|
||||
function Tagged<T extends Constructor<{}>>(
|
||||
superClass: T
|
||||
): Constructor<Tagged> & 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<T> = 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 = <T extends Constructor<Base>>(superClass: T) => class extends superClass {
|
||||
static message = "hello";
|
||||
print() {
|
||||
const output = this.x + "," + this.y;
|
||||
}
|
||||
}
|
||||
|
||||
function Tagged<T extends Constructor<{}>>(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 = <CT extends Constructor<object>>(Base: CT) => {
|
||||
return class extends Base {
|
||||
timestamp = new Date();
|
||||
};
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
type Constructor<T> = 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 = <T extends Constructor<Base>>(superClass: T) => class
|
||||
extends superClass {
|
||||
static message = "hello";
|
||||
print() {
|
||||
const output = this.x + "," + this.y;
|
||||
}
|
||||
};
|
||||
|
||||
function Tagged<T extends Constructor<{}>>(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 = <CT extends Constructor<object>>(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 {}
|
||||
}
|
||||
|
||||
`;
|
|
@ -0,0 +1,11 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`classInsideBlock.ts 1`] = `
|
||||
function foo() {
|
||||
class C { }
|
||||
}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
function foo() {
|
||||
class C {}
|
||||
}
|
||||
|
||||
`;
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<T> {
|
||||
foo: T;
|
||||
thing(x: T) { }
|
||||
static other<T>(x: T) { }
|
||||
}
|
||||
|
||||
class D2<T> extends C2<T> {
|
||||
bar: string;
|
||||
}
|
||||
|
||||
var d2: D2<string>;
|
||||
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<T> {
|
||||
foo: T;
|
||||
thing(x: T) {}
|
||||
static other<T>(x: T) {}
|
||||
}
|
||||
|
||||
class D2<T> extends C2<T> {
|
||||
bar: string;
|
||||
}
|
||||
|
||||
var d2: D2<string>;
|
||||
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<T> extends E2<T> { foo: T; } // error
|
||||
|
||||
class D2<T> extends C2<T> { bar: T; }
|
||||
|
||||
class E2<T> extends D2<T> { baz: T; }~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
class C extends E {
|
||||
foo: string;
|
||||
} // error
|
||||
|
||||
class D extends C {
|
||||
bar: string;
|
||||
}
|
||||
|
||||
class E extends D {
|
||||
baz: number;
|
||||
}
|
||||
|
||||
class C2<T> extends E2<T> {
|
||||
foo: T;
|
||||
} // error
|
||||
|
||||
class D2<T> extends C2<T> {
|
||||
bar: T;
|
||||
}
|
||||
|
||||
class E2<T> extends D2<T> {
|
||||
baz: T;
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
exports[`classIsSubtypeOfBaseType.ts 1`] = `
|
||||
class Base<T> {
|
||||
foo: T;
|
||||
}
|
||||
|
||||
class Derived extends Base<string> {
|
||||
foo: any;
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
class Base<T> {
|
||||
foo: T;
|
||||
}
|
||||
|
||||
class Derived extends Base<string> {
|
||||
foo: any;
|
||||
}
|
||||
|
||||
`;
|
|
@ -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;
|
|
@ -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<T> {
|
||||
foo: T;
|
||||
thing(x: T) { }
|
||||
static other<T>(x: T) { }
|
||||
}
|
||||
|
||||
class D2<T> extends C2<T> {
|
||||
bar: string;
|
||||
}
|
||||
|
||||
var d2: D2<string>;
|
||||
var r5 = d2.foo;
|
||||
var r6 = d2.bar;
|
||||
var r7 = d2.thing('');
|
||||
var r8 = D2.other(1);
|
|
@ -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<T> extends E2<T> { foo: T; } // error
|
||||
|
||||
class D2<T> extends C2<T> { bar: T; }
|
||||
|
||||
class E2<T> extends D2<T> { baz: T; }
|
|
@ -0,0 +1,7 @@
|
|||
class Base<T> {
|
||||
foo: T;
|
||||
}
|
||||
|
||||
class Derived extends Base<string> {
|
||||
foo: any;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
run_spec(__dirname, { parser: "typescript" });
|
|
@ -0,0 +1,3 @@
|
|||
function foo() {
|
||||
class C { }
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
run_spec(__dirname, { parser: "typescript" });
|
|
@ -0,0 +1,10 @@
|
|||
var x = class C {
|
||||
}
|
||||
|
||||
var y = {
|
||||
foo: class C2 {
|
||||
}
|
||||
}
|
||||
|
||||
var z = class C4 {
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
run_spec(__dirname, { parser: "typescript" });
|
|
@ -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<T, U>(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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// @declaration: true
|
||||
|
||||
type Constructor<T> = 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 = <T extends Constructor<Base>>(superClass: T): Constructor<Printable> & { message: string } & T =>
|
||||
class extends superClass {
|
||||
static message = "hello";
|
||||
print() {
|
||||
const output = this.x + "," + this.y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function Tagged<T extends Constructor<{}>>(superClass: T): Constructor<Tagged> & 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
type Constructor<T> = 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 = <T extends Constructor<Base>>(superClass: T) => class extends superClass {
|
||||
static message = "hello";
|
||||
print() {
|
||||
const output = this.x + "," + this.y;
|
||||
}
|
||||
}
|
||||
|
||||
function Tagged<T extends Constructor<{}>>(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 = <CT extends Constructor<object>>(Base: CT) => {
|
||||
return class extends Base {
|
||||
timestamp = new Date();
|
||||
};
|
||||
}
|
|
@ -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(); }
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// nested classes are not allowed
|
||||
|
||||
class C {
|
||||
x: string;
|
||||
}
|
||||
|
||||
function foo() {
|
||||
class C3 {
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ declare class MyArray<T> extends Array<T> {
|
|||
sort(compareFn?: (a: T, b: T) => number): this;
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
declare class MyArray<T> extends Array {
|
||||
declare class MyArray<T> extends Array<T> {
|
||||
sort(compareFn?: (a: T, b: T) => number): this;
|
||||
}
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ class C2<T extends Date, U extends Date> {
|
|||
// no errors expected
|
||||
|
||||
class C<T extends Date> {
|
||||
g() {
|
||||
g<T extends Number>() {
|
||||
var x: T;
|
||||
x.toFixed();
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ class C<T extends Date> {
|
|||
}
|
||||
|
||||
class C2<T extends Date, U extends Date> {
|
||||
g() {
|
||||
g<T extends Number, U extends Number>() {
|
||||
var x: U;
|
||||
x.toFixed();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
`;
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue