Compare commits

...

1 commit

Author SHA1 Message Date
Nathan Shively-Sanders 20b4c99b27 Backward-compatible accessor emit
Accessors are now only emitted in d.ts when useDefineForClassFields is set.
Otherwise, properties that originated as accessors are prefixed with
a JSDoc tag: `/**@accessor*/`

The checker now understands `/**@accessor*/` tags for the purpose of
error reporting; if --useDefineForClassFields is provided, an ambient
base property with `/**@accessor*/` will be treated as an accessor, not
as a property.
2019-10-08 13:20:34 -07:00
34 changed files with 311 additions and 161 deletions

View file

@ -31642,7 +31642,8 @@ namespace ts {
if (basePropertyFlags !== SymbolFlags.Property && derivedPropertyFlags === SymbolFlags.Property) {
errorMessage = Diagnostics.Class_0_defines_instance_member_accessor_1_but_extended_class_2_defines_it_as_instance_member_property;
}
else if (basePropertyFlags === SymbolFlags.Property && derivedPropertyFlags !== SymbolFlags.Property) {
else if (basePropertyFlags === SymbolFlags.Property && derivedPropertyFlags !== SymbolFlags.Property
&& !(base.valueDeclaration && base.valueDeclaration.flags & NodeFlags.Ambient && getJSDocTags(base.valueDeclaration).find(tag => tag.tagName.escapedText === "accessor"))) {
errorMessage = Diagnostics.Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_accessor;
}
else {

View file

@ -83,6 +83,7 @@ namespace ts {
let emittedImports: readonly AnyImportSyntax[] | undefined; // must be declared in container so it can be `undefined` while transformer's first pass
const resolver = context.getEmitResolver();
const options = context.getCompilerOptions();
const newLine = getNewLineCharacter(options);
const { noResolve, stripInternal } = options;
return transformRoot;
@ -864,25 +865,35 @@ namespace ts {
return cleanup(sig);
}
case SyntaxKind.GetAccessor: {
const isPrivate = hasModifier(input, ModifierFlags.Private);
const accessorType = getTypeAnnotationFromAllAccessorDeclarations(input, resolver.getAllAccessorDeclarations(input));
return cleanup(updateGetAccessor(
input,
/*decorators*/ undefined,
ensureModifiers(input),
input.name,
updateAccessorParamsList(input, isPrivate),
!isPrivate ? ensureType(input, accessorType) : undefined,
/*body*/ undefined));
if (options.useDefineForClassFields) {
const isPrivate = hasModifier(input, ModifierFlags.Private);
const accessorType = getTypeAnnotationFromAllAccessorDeclarations(input, resolver.getAllAccessorDeclarations(input));
return cleanup(updateGetAccessor(
input,
/*decorators*/ undefined,
ensureModifiers(input),
input.name,
updateAccessorParamsList(input, isPrivate),
!isPrivate ? ensureType(input, accessorType) : undefined,
/*body*/ undefined));
}
else {
return cleanup(ensureAccessor(input));
}
}
case SyntaxKind.SetAccessor: {
return cleanup(updateSetAccessor(
input,
/*decorators*/ undefined,
ensureModifiers(input),
input.name,
updateAccessorParamsList(input, hasModifier(input, ModifierFlags.Private)),
/*body*/ undefined));
if (options.useDefineForClassFields) {
return cleanup(updateSetAccessor(
input,
/*decorators*/ undefined,
ensureModifiers(input),
input.name,
updateAccessorParamsList(input, hasModifier(input, ModifierFlags.Private)),
/*body*/ undefined));
}
else {
return cleanup(ensureAccessor(input));
}
}
case SyntaxKind.PropertyDeclaration:
return cleanup(updateProperty(
@ -1455,6 +1466,42 @@ namespace ts {
return accessorType;
}
function ensureAccessor(node: AccessorDeclaration): PropertyDeclaration | undefined {
const accessors = resolver.getAllAccessorDeclarations(node);
if (node.kind !== accessors.firstAccessor.kind) {
return;
}
const accessorType = getTypeAnnotationFromAllAccessorDeclarations(node, accessors);
const prop = createProperty(/*decorators*/ undefined, maskModifiers(node, /*mask*/ undefined, (!accessors.setAccessor) ? ModifierFlags.Readonly : ModifierFlags.None), node.name, node.questionToken, ensureType(node, accessorType), /*initializer*/ undefined);
const leadingsSyntheticCommentRanges = accessors.secondAccessor && getLeadingCommentRangesOfNode(accessors.secondAccessor, currentSourceFile);
if (leadingsSyntheticCommentRanges) {
for (const range of leadingsSyntheticCommentRanges) {
if (range.kind === SyntaxKind.MultiLineCommentTrivia) {
let text = currentSourceFile.text.slice(range.pos + 2, range.end - 2);
const lines = text.split(/\r\n?|\n/g);
if (lines.length > 1) {
const lastLines = lines.slice(1);
const indentation = guessIndentation(lastLines);
text = [lines[0], ...map(lastLines, l => l.slice(indentation))].join(newLine);
}
addSyntheticLeadingComment(
prop,
range.kind,
text,
range.hasTrailingNewLine
);
}
}
}
addSyntheticLeadingComment(
prop,
SyntaxKind.MultiLineCommentTrivia,
"*@accessor",
/*hasTrailingNewLine*/ false
);
return prop;
}
function transformHeritageClauses(nodes: NodeArray<HeritageClause> | undefined) {
return createNodeArray(filter(map(nodes, clause => updateHeritageClause(clause, visitNodes(createNodeArray(filter(clause.types, t => {
return isEntityNameExpression(t.expression) || (clause.token === SyntaxKind.ExtendsKeyword && t.expression.kind === SyntaxKind.NullKeyword);

View file

@ -1,5 +1,5 @@
tests/cases/compiler/accessorDeclarationEmitVisibilityErrors.ts(2,18): error TS2304: Cannot find name 'DoesNotExist'.
tests/cases/compiler/accessorDeclarationEmitVisibilityErrors.ts(2,18): error TS4106: Parameter 'arg' of accessor has or is using private name 'DoesNotExist'.
tests/cases/compiler/accessorDeclarationEmitVisibilityErrors.ts(2,18): error TS4037: Parameter type of public setter 'bet' from exported class has or is using private name 'DoesNotExist'.
==== tests/cases/compiler/accessorDeclarationEmitVisibilityErrors.ts (2 errors) ====
@ -8,5 +8,5 @@ tests/cases/compiler/accessorDeclarationEmitVisibilityErrors.ts(2,18): error TS4
~~~~~~~~~~~~
!!! error TS2304: Cannot find name 'DoesNotExist'.
~~~~~~~~~~~~
!!! error TS4106: Parameter 'arg' of accessor has or is using private name 'DoesNotExist'.
!!! error TS4037: Parameter type of public setter 'bet' from exported class has or is using private name 'DoesNotExist'.
}

View file

@ -10,13 +10,21 @@ class D {
}
}
class E {
// comment 1
get x() { return 2; }
// comment 2
set x(v) { }
}
var x = {
get a() { return 1 }
}
var y = {
set b(v) { }
}
}
//// [accessorWithES5.js]
var C = /** @class */ (function () {
@ -42,9 +50,40 @@ var D = /** @class */ (function () {
});
return D;
}());
var E = /** @class */ (function () {
function E() {
}
Object.defineProperty(E.prototype, "x", {
// comment 1
get: function () { return 2; },
// comment 2
set: function (v) { },
enumerable: true,
configurable: true
});
return E;
}());
var x = {
get a() { return 1; }
};
var y = {
set b(v) { }
};
//// [accessorWithES5.d.ts]
declare class C {
/**@accessor*/ readonly x: number;
}
declare class D {
/**@accessor*/ x: any;
}
declare class E {
/**@accessor*/ x: number;
}
declare var x: {
readonly a: number;
};
declare var y: {
b: any;
};

View file

@ -18,17 +18,31 @@ class D {
}
}
class E {
>E : Symbol(E, Decl(accessorWithES5.ts, 9, 1))
// comment 1
get x() { return 2; }
>x : Symbol(E.x, Decl(accessorWithES5.ts, 11, 9), Decl(accessorWithES5.ts, 13, 25))
// comment 2
set x(v) { }
>x : Symbol(E.x, Decl(accessorWithES5.ts, 11, 9), Decl(accessorWithES5.ts, 13, 25))
>v : Symbol(v, Decl(accessorWithES5.ts, 15, 10))
}
var x = {
>x : Symbol(x, Decl(accessorWithES5.ts, 11, 3))
>x : Symbol(x, Decl(accessorWithES5.ts, 18, 3))
get a() { return 1 }
>a : Symbol(a, Decl(accessorWithES5.ts, 11, 9))
>a : Symbol(a, Decl(accessorWithES5.ts, 18, 9))
}
var y = {
>y : Symbol(y, Decl(accessorWithES5.ts, 15, 3))
>y : Symbol(y, Decl(accessorWithES5.ts, 22, 3))
set b(v) { }
>b : Symbol(b, Decl(accessorWithES5.ts, 15, 9))
>v : Symbol(v, Decl(accessorWithES5.ts, 16, 10))
>b : Symbol(b, Decl(accessorWithES5.ts, 22, 9))
>v : Symbol(v, Decl(accessorWithES5.ts, 23, 10))
}

View file

@ -19,6 +19,20 @@ class D {
}
}
class E {
>E : E
// comment 1
get x() { return 2; }
>x : number
>2 : 2
// comment 2
set x(v) { }
>x : number
>v : number
}
var x = {
>x : { readonly a: number; }
>{ get a() { return 1 }} : { readonly a: number; }
@ -36,3 +50,4 @@ var y = {
>b : any
>v : any
}

View file

@ -19,12 +19,8 @@ declare class C {
//// [ambientAccessors.d.ts]
declare class C {
static get a(): string;
static set a(value: string);
private static get b();
private static set b(value);
get x(): string;
set x(value: string);
private get y();
private set y(value);
/**@accessor*/ static a: string;
/**@accessor*/ private static b;
/**@accessor*/ x: string;
/**@accessor*/ private y;
}

View file

@ -19,12 +19,8 @@ declare class C {
//// [ambientAccessors.d.ts]
declare class C {
static get a(): string;
static set a(value: string);
private static get b();
private static set b(value);
get x(): string;
set x(value: string);
private get y();
private set y(value);
/**@accessor*/ static a: string;
/**@accessor*/ private static b;
/**@accessor*/ x: string;
/**@accessor*/ private y;
}

View file

@ -303,9 +303,7 @@ declare let Q1: new (x: unknown) => x is string;
declare let Q2: new (x: boolean) => asserts x;
declare let Q3: new (x: unknown) => asserts x is string;
declare class Wat {
get p1(): this is string;
set p1(x: this is string);
get p2(): asserts this is string;
set p2(x: asserts this is string);
/**@accessor*/ p1: this is string;
/**@accessor*/ p2: asserts this is string;
}
declare function f20(x: unknown): void;

View file

@ -218,14 +218,13 @@ declare class a {
constructor(s: string);
pgF(): void;
pv: any;
get d(): number;
set d(a: number);
static get p2(): {
/**@accessor*/ d: number;
/**@accessor*/ static readonly p2: {
x: number;
y: number;
};
private static d2;
private static get p3();
/**@accessor*/ private static readonly p3;
private pv3;
private foo;
}

View file

@ -144,8 +144,7 @@ declare class c {
constructor();
b: number;
myFoo(): number;
get prop1(): number;
set prop1(val: number);
prop1: number;
foo1(a: number): string;
foo1(b: string): string;
}

View file

@ -160,9 +160,8 @@ declare class c {
/** function comment */
myFoo(): number;
/** getter comment*/
get prop1(): number;
/** setter comment*/
set prop1(val: number);
/**@accessor*/ prop1: number;
/** overload signature1*/
foo1(a: number): string;
/** Overload signature 2*/

View file

@ -273,47 +273,35 @@ var c2 = /** @class */ (function () {
/** This is comment for c1*/
export declare class c1 {
/** getter property*/
get p3(): number;
/** setter property*/
set p3(/** this is value*/ value: number);
/**@accessor*/ p3: number;
/** private getter property*/
private get pp3();
/** private setter property*/
private set pp3(value);
/**@accessor*/ private pp3;
/** static getter property*/
static get s3(): number;
/** setter property*/
static set s3(/** this is value*/ value: number);
get nc_p3(): number;
set nc_p3(value: number);
private get nc_pp3();
private set nc_pp3(value);
static get nc_s3(): string;
static set nc_s3(value: string);
get onlyGetter(): number;
set onlySetter(value: number);
/**@accessor*/ static s3: number;
/**@accessor*/ nc_p3: number;
/**@accessor*/ private nc_pp3;
/**@accessor*/ static nc_s3: string;
/**@accessor*/ readonly onlyGetter: number;
/**@accessor*/ onlySetter: number;
}
//// [declFileAccessors_1.d.ts]
/** This is comment for c2 - the global class*/
declare class c2 {
/** getter property*/
get p3(): number;
/** setter property*/
set p3(/** this is value*/ value: number);
/**@accessor*/ p3: number;
/** private getter property*/
private get pp3();
/** private setter property*/
private set pp3(value);
/**@accessor*/ private pp3;
/** static getter property*/
static get s3(): number;
/** setter property*/
static set s3(/** this is value*/ value: number);
get nc_p3(): number;
set nc_p3(value: number);
private get nc_pp3();
private set nc_pp3(value);
static get nc_s3(): string;
static set nc_s3(value: string);
get onlyGetter(): number;
set onlySetter(value: number);
/**@accessor*/ static s3: number;
/**@accessor*/ nc_p3: number;
/**@accessor*/ private nc_pp3;
/**@accessor*/ static nc_s3: string;
/**@accessor*/ readonly onlyGetter: number;
/**@accessor*/ onlySetter: number;
}

View file

@ -51,8 +51,8 @@ declare class C {
static y: number;
private static a;
static b(): void;
private static get c();
static get d(): number;
private static set e(value);
static set f(v: any);
/**@accessor*/ private static readonly c;
/**@accessor*/ static readonly d: number;
/**@accessor*/ private static e;
/**@accessor*/ static f: any;
}

View file

@ -271,27 +271,21 @@ declare module m {
}
}
export class c {
get foo1(): private1;
get foo2(): private1;
set foo3(param: private1);
get foo4(): private1;
set foo4(param: private1);
get foo5(): private1;
set foo5(param: private1);
get foo11(): public1;
get foo12(): public1;
set foo13(param: public1);
get foo14(): public1;
set foo14(param: public1);
get foo15(): public1;
set foo15(param: public1);
get foo111(): m2.public2;
get foo112(): m2.public2;
set foo113(param: m2.public2);
get foo114(): m2.public2;
set foo114(param: m2.public2);
get foo115(): m2.public2;
set foo115(param: m2.public2);
/**@accessor*/ readonly foo1: private1;
/**@accessor*/ readonly foo2: private1;
/**@accessor*/ foo3: private1;
/**@accessor*/ foo4: private1;
/**@accessor*/ foo5: private1;
/**@accessor*/ readonly foo11: public1;
/**@accessor*/ readonly foo12: public1;
/**@accessor*/ foo13: public1;
/**@accessor*/ foo14: public1;
/**@accessor*/ foo15: public1;
/**@accessor*/ readonly foo111: m2.public2;
/**@accessor*/ readonly foo112: m2.public2;
/**@accessor*/ foo113: m2.public2;
/**@accessor*/ foo114: m2.public2;
/**@accessor*/ foo115: m2.public2;
}
export {};
}

View file

@ -103,10 +103,10 @@ export declare class C2 {
bar(): (t: typeof C2) => void;
}
export declare class C3 {
get C3(): number;
/**@accessor*/ readonly C3: number;
bar(): (t: typeof C3) => void;
}
export declare class C4 {
set C4(v: any);
/**@accessor*/ C4: any;
bar(): (t: typeof C4) => void;
}

View file

@ -138,12 +138,11 @@ var C4 = /** @class */ (function () {
declare class C1 {
protected x: number;
protected f(): number;
protected set accessor(a: number);
protected get accessor(): number;
/**@accessor*/ protected accessor: number;
protected static sx: number;
protected static sf(): number;
protected static set staticSetter(a: number);
protected static get staticGetter(): number;
/**@accessor*/ protected static staticSetter: number;
/**@accessor*/ protected static readonly staticGetter: number;
}
declare class C2 extends C1 {
protected f(): number;
@ -154,7 +153,7 @@ declare class C3 extends C2 {
static sx: number;
f(): number;
static sf(): number;
static get staticGetter(): number;
/**@accessor*/ static readonly staticGetter: number;
}
declare class C4 {
protected a: number;

View file

@ -101,12 +101,12 @@ export interface InterfaceMemberVisibility {
export declare class ClassMemberVisibility {
static [x]: number;
static [y](): number;
static get [z](): number;
static set [w](value: number);
/**@accessor*/ static readonly [z]: number;
/**@accessor*/ static [w]: number;
[x]: number;
[y](): number;
get [z](): number;
set [w](value: number);
/**@accessor*/ readonly [z]: number;
/**@accessor*/ [w]: number;
}
export declare type ObjectTypeVisibility = {
[x]: number;

View file

@ -184,7 +184,7 @@ declare module schema {
}
declare module schema {
class T {
get createValidator9(): <T>(data: T) => T;
set createValidator10(v: <T>(data: T) => T);
/**@accessor*/ readonly createValidator9: <T>(data: T) => T;
/**@accessor*/ createValidator10: <T>(data: T) => T;
}
}

View file

@ -32,6 +32,5 @@ var MyClass = /** @class */ (function () {
//// [properties.d.ts]
declare class MyClass {
get Count(): number;
set Count(value: number);
/**@accessor*/ Count: number;
}

View file

@ -0,0 +1,18 @@
//// [tests/cases/conformance/classes/propertyMemberDeclarations/propertyOverridesAccessors6.ts] ////
//// [a.d.ts]
declare class A {
/**@accessor*/ p: number
}
//// [b.ts]
class B extends A {
get p() { return 1 }
set p(value) { }
}
//// [b.js]
class B extends A {
get p() { return 1; }
set p(value) { }
}

View file

@ -0,0 +1,20 @@
=== tests/cases/conformance/classes/propertyMemberDeclarations/a.d.ts ===
declare class A {
>A : Symbol(A, Decl(a.d.ts, 0, 0))
/**@accessor*/ p: number
>p : Symbol(A.p, Decl(a.d.ts, 0, 17))
}
=== tests/cases/conformance/classes/propertyMemberDeclarations/b.ts ===
class B extends A {
>B : Symbol(B, Decl(b.ts, 0, 0))
>A : Symbol(A, Decl(a.d.ts, 0, 0))
get p() { return 1 }
>p : Symbol(B.p, Decl(b.ts, 0, 19), Decl(b.ts, 1, 24))
set p(value) { }
>p : Symbol(B.p, Decl(b.ts, 0, 19), Decl(b.ts, 1, 24))
>value : Symbol(value, Decl(b.ts, 2, 10))
}

View file

@ -0,0 +1,21 @@
=== tests/cases/conformance/classes/propertyMemberDeclarations/a.d.ts ===
declare class A {
>A : A
/**@accessor*/ p: number
>p : number
}
=== tests/cases/conformance/classes/propertyMemberDeclarations/b.ts ===
class B extends A {
>B : B
>A : A
get p() { return 1 }
>p : number
>1 : 1
set p(value) { }
>p : number
>value : number
}

View file

@ -149,27 +149,21 @@ declare class C {
private readonly a1;
protected readonly a2: number;
readonly a3: number;
private get b1();
protected get b2(): number;
get b3(): number;
private get c1();
private set c1(value);
protected get c2(): number;
protected set c2(value: number);
get c3(): number;
set c3(value: number);
/**@accessor*/ private readonly b1;
/**@accessor*/ protected readonly b2: number;
/**@accessor*/ readonly b3: number;
/**@accessor*/ private c1;
/**@accessor*/ protected c2: number;
/**@accessor*/ c3: number;
private static readonly s1;
protected static readonly s2: number;
static readonly s3: number;
private static get t1();
protected static get t2(): number;
static get t3(): number;
private static get u1();
private static set u1(value);
protected static get u2(): number;
protected static set u2(value: number);
static get u3(): number;
static set u3(value: number);
/**@accessor*/ private static readonly t1;
/**@accessor*/ protected static readonly t2: number;
/**@accessor*/ static readonly t3: number;
/**@accessor*/ private static u1;
/**@accessor*/ protected static u2: number;
/**@accessor*/ static u3: number;
}
declare var z: {
readonly a: string;

View file

@ -19,6 +19,5 @@ C[Symbol.iterator] = 0;
declare class C {
static [Symbol.iterator]: number;
static [Symbol.isConcatSpreadable](): void;
static get [Symbol.toPrimitive](): string;
static set [Symbol.toPrimitive](x: string);
/**@accessor*/ static [Symbol.toPrimitive]: string;
}

View file

@ -35,8 +35,8 @@ declare module M {
[Symbol.iterator]: I;
[Symbol.toPrimitive](x: I): void;
[Symbol.isConcatSpreadable](): I;
get [Symbol.toPrimitive](): any;
set [Symbol.toPrimitive](x: I);
/**@accessor*/ readonly [Symbol.toPrimitive]: any;
/**@accessor*/ [Symbol.toPrimitive]: I;
}
export {};
}

View file

@ -13,6 +13,6 @@ class C {
//// [symbolDeclarationEmit13.d.ts]
declare class C {
get [Symbol.toPrimitive](): string;
set [Symbol.toStringTag](x: any);
/**@accessor*/ readonly [Symbol.toPrimitive]: string;
/**@accessor*/ [Symbol.toStringTag]: any;
}

View file

@ -13,6 +13,6 @@ class C {
//// [symbolDeclarationEmit14.d.ts]
declare class C {
get [Symbol.toPrimitive](): string;
get [Symbol.toStringTag](): string;
/**@accessor*/ readonly [Symbol.toPrimitive]: string;
/**@accessor*/ readonly [Symbol.toStringTag]: string;
}

View file

@ -13,6 +13,5 @@ class C {
//// [symbolDeclarationEmit4.d.ts]
declare class C {
get [Symbol.toPrimitive](): string;
set [Symbol.toPrimitive](x: string);
/**@accessor*/ [Symbol.toPrimitive]: string;
}

View file

@ -178,9 +178,8 @@ declare namespace Test {
class FileSystemObject {
path: string;
isFSO: this is FileSystemObject;
get isFile(): this is File;
set isFile(param: this is File);
get isDirectory(): this is Directory;
/**@accessor*/ isFile: this is File;
/**@accessor*/ readonly isDirectory: this is Directory;
isNetworked: this is (Networked & this);
constructor(path: string);
}

View file

@ -103,9 +103,8 @@ declare namespace Test {
class FileSystemObject {
path: string;
isFSO: this is FileSystemObject;
get isFile(): this is File;
set isFile(param: this is File);
get isDirectory(): this is Directory;
/**@accessor*/ isFile: this is File;
/**@accessor*/ readonly isDirectory: this is Directory;
isNetworked: this is (Networked & this);
constructor(path: string);
}

View file

@ -141,9 +141,7 @@ export declare class ClassWithPrivateNamedMethods {
static [s](): void;
}
export declare class ClassWithPrivateNamedAccessors {
get [s](): any;
set [s](v: any);
static get [s](): any;
static set [s](v: any);
/**@accessor*/ [s]: any;
/**@accessor*/ static [s]: any;
}
export {};

View file

@ -1,4 +1,6 @@
// @target: ES5
// @useDefineForClassFields: false
// @declaration: true
class C {
get x() {
@ -11,10 +13,17 @@ class D {
}
}
class E {
// comment 1
get x() { return 2; }
// comment 2
set x(v) { }
}
var x = {
get a() { return 1 }
}
var y = {
set b(v) { }
}
}

View file

@ -0,0 +1,11 @@
// @target: esnext
// @useDefineForClassFields: true
// @Filename: a.d.ts
declare class A {
/**@accessor*/ p: number
}
// @Filename: b.ts
class B extends A {
get p() { return 1 }
set p(value) { }
}