diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 60ecfdb2cf1..ed68db53803 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33082,13 +33082,15 @@ namespace ts { function checkGrammarAccessor(accessor: AccessorLike): boolean { if (isAccessorDeclaration(accessor)) { - if (languageVersion < ScriptTarget.ES5) { - return grammarErrorOnNode(accessor.name, Diagnostics.Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher); + if (!(accessor.flags & NodeFlags.Ambient)) { + if (languageVersion < ScriptTarget.ES5) { + return grammarErrorOnNode(accessor.name, Diagnostics.Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher); + } + else if (accessor.body === undefined && !hasModifier(accessor, ModifierFlags.Abstract) && !(accessor.flags & NodeFlags.Ambient)) { + return grammarErrorAtPos(accessor, accessor.end - 1, ";".length, Diagnostics._0_expected, "{"); + } } - else if (accessor.body === undefined && !hasModifier(accessor, ModifierFlags.Abstract) && !(accessor.flags & NodeFlags.Ambient)) { - return grammarErrorAtPos(accessor, accessor.end - 1, ";".length, Diagnostics._0_expected, "{"); - } - else if (accessor.body && hasModifier(accessor, ModifierFlags.Abstract)) { + if (accessor.body && hasModifier(accessor, ModifierFlags.Abstract)) { return grammarErrorOnNode(accessor, Diagnostics.An_abstract_accessor_cannot_have_an_implementation); } } diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index d834e29f2f3..ea0b5488cec 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -830,10 +830,32 @@ namespace ts { return cleanup(sig); } case SyntaxKind.GetAccessor: { + // For now, only emit class accessors as accessors if they were already declared in an ambient context. + if (input.flags & NodeFlags.Ambient) { + const accessorType = getTypeAnnotationFromAllAccessorDeclarations(input); + return cleanup(updateGetAccessor( + input, + /*decorators*/ undefined, + ensureModifiers(input), + input.name, + updateParamsList(input, input.parameters), + !hasModifier(input, ModifierFlags.Private) ? ensureType(input, accessorType) : undefined, + /*body*/ undefined)); + } const newNode = ensureAccessor(input); return cleanup(newNode); } case SyntaxKind.SetAccessor: { + // For now, only emit class accessors as accessors if they were already declared in an ambient context. + if (input.flags & NodeFlags.Ambient) { + return cleanup(updateSetAccessor( + input, + /*decorators*/ undefined, + ensureModifiers(input), + input.name, + updateParamsList(input, input.parameters), + /*body*/ undefined)); + } const newNode = ensureAccessor(input); return cleanup(newNode); } diff --git a/tests/baselines/reference/ambientAccessors(target=es3).js b/tests/baselines/reference/ambientAccessors(target=es3).js new file mode 100644 index 00000000000..120e8719270 --- /dev/null +++ b/tests/baselines/reference/ambientAccessors(target=es3).js @@ -0,0 +1,19 @@ +//// [ambientAccessors.ts] +// ok to use accessors in ambient class in ES3 +declare class C { + get x(): string; + set x(value: string); + static get y(): string; + static set y(value: string); +} + +//// [ambientAccessors.js] + + +//// [ambientAccessors.d.ts] +declare class C { + get x(): string; + set x(value: string); + static get y(): string; + static set y(value: string); +} diff --git a/tests/baselines/reference/ambientAccessors(target=es3).symbols b/tests/baselines/reference/ambientAccessors(target=es3).symbols new file mode 100644 index 00000000000..6076622b825 --- /dev/null +++ b/tests/baselines/reference/ambientAccessors(target=es3).symbols @@ -0,0 +1,19 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/memberAccessorDeclarations/ambientAccessors.ts === +// ok to use accessors in ambient class in ES3 +declare class C { +>C : Symbol(C, Decl(ambientAccessors.ts, 0, 0)) + + get x(): string; +>x : Symbol(C.x, Decl(ambientAccessors.ts, 1, 17), Decl(ambientAccessors.ts, 2, 20)) + + set x(value: string); +>x : Symbol(C.x, Decl(ambientAccessors.ts, 1, 17), Decl(ambientAccessors.ts, 2, 20)) +>value : Symbol(value, Decl(ambientAccessors.ts, 3, 10)) + + static get y(): string; +>y : Symbol(C.y, Decl(ambientAccessors.ts, 3, 25), Decl(ambientAccessors.ts, 4, 27)) + + static set y(value: string); +>y : Symbol(C.y, Decl(ambientAccessors.ts, 3, 25), Decl(ambientAccessors.ts, 4, 27)) +>value : Symbol(value, Decl(ambientAccessors.ts, 5, 17)) +} diff --git a/tests/baselines/reference/ambientAccessors(target=es3).types b/tests/baselines/reference/ambientAccessors(target=es3).types new file mode 100644 index 00000000000..dc5fb2c7d6c --- /dev/null +++ b/tests/baselines/reference/ambientAccessors(target=es3).types @@ -0,0 +1,19 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/memberAccessorDeclarations/ambientAccessors.ts === +// ok to use accessors in ambient class in ES3 +declare class C { +>C : C + + get x(): string; +>x : string + + set x(value: string); +>x : string +>value : string + + static get y(): string; +>y : string + + static set y(value: string); +>y : string +>value : string +} diff --git a/tests/baselines/reference/ambientAccessors(target=es5).js b/tests/baselines/reference/ambientAccessors(target=es5).js new file mode 100644 index 00000000000..120e8719270 --- /dev/null +++ b/tests/baselines/reference/ambientAccessors(target=es5).js @@ -0,0 +1,19 @@ +//// [ambientAccessors.ts] +// ok to use accessors in ambient class in ES3 +declare class C { + get x(): string; + set x(value: string); + static get y(): string; + static set y(value: string); +} + +//// [ambientAccessors.js] + + +//// [ambientAccessors.d.ts] +declare class C { + get x(): string; + set x(value: string); + static get y(): string; + static set y(value: string); +} diff --git a/tests/baselines/reference/ambientAccessors(target=es5).symbols b/tests/baselines/reference/ambientAccessors(target=es5).symbols new file mode 100644 index 00000000000..6076622b825 --- /dev/null +++ b/tests/baselines/reference/ambientAccessors(target=es5).symbols @@ -0,0 +1,19 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/memberAccessorDeclarations/ambientAccessors.ts === +// ok to use accessors in ambient class in ES3 +declare class C { +>C : Symbol(C, Decl(ambientAccessors.ts, 0, 0)) + + get x(): string; +>x : Symbol(C.x, Decl(ambientAccessors.ts, 1, 17), Decl(ambientAccessors.ts, 2, 20)) + + set x(value: string); +>x : Symbol(C.x, Decl(ambientAccessors.ts, 1, 17), Decl(ambientAccessors.ts, 2, 20)) +>value : Symbol(value, Decl(ambientAccessors.ts, 3, 10)) + + static get y(): string; +>y : Symbol(C.y, Decl(ambientAccessors.ts, 3, 25), Decl(ambientAccessors.ts, 4, 27)) + + static set y(value: string); +>y : Symbol(C.y, Decl(ambientAccessors.ts, 3, 25), Decl(ambientAccessors.ts, 4, 27)) +>value : Symbol(value, Decl(ambientAccessors.ts, 5, 17)) +} diff --git a/tests/baselines/reference/ambientAccessors(target=es5).types b/tests/baselines/reference/ambientAccessors(target=es5).types new file mode 100644 index 00000000000..dc5fb2c7d6c --- /dev/null +++ b/tests/baselines/reference/ambientAccessors(target=es5).types @@ -0,0 +1,19 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/memberAccessorDeclarations/ambientAccessors.ts === +// ok to use accessors in ambient class in ES3 +declare class C { +>C : C + + get x(): string; +>x : string + + set x(value: string); +>x : string +>value : string + + static get y(): string; +>y : string + + static set y(value: string); +>y : string +>value : string +} diff --git a/tests/cases/conformance/classes/propertyMemberDeclarations/memberAccessorDeclarations/ambientAccessors.ts b/tests/cases/conformance/classes/propertyMemberDeclarations/memberAccessorDeclarations/ambientAccessors.ts new file mode 100644 index 00000000000..a13ea412df6 --- /dev/null +++ b/tests/cases/conformance/classes/propertyMemberDeclarations/memberAccessorDeclarations/ambientAccessors.ts @@ -0,0 +1,9 @@ +// @target: es3, es5 +// @declaration: true +// ok to use accessors in ambient class in ES3 +declare class C { + get x(): string; + set x(value: string); + static get y(): string; + static set y(value: string); +} \ No newline at end of file diff --git a/tests/cases/conformance/interfaces/interfaceDeclarations/interfaceWithAccessors.ts b/tests/cases/conformance/interfaces/interfaceDeclarations/interfaceWithAccessors.ts new file mode 100644 index 00000000000..087c7fca01f --- /dev/null +++ b/tests/cases/conformance/interfaces/interfaceDeclarations/interfaceWithAccessors.ts @@ -0,0 +1,15 @@ +// @target: esnext +// @declaration: true +interface I0 { + get x(): number; + set x(value); +} + +interface I1 { + get x(): number; +} + +interface I1 { + set x(value); +} +