From 2c7005169162ee33d9cb72cd4992cdca87f38de8 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 4 May 2016 11:02:54 -0700 Subject: [PATCH 1/3] Allow this parameters for accessors Also refactor getSignatureFromDeclaration a bit --- src/compiler/checker.ts | 139 +++++++++++++++++---------- src/compiler/diagnosticMessages.json | 2 +- src/compiler/utilities.ts | 7 +- 3 files changed, 94 insertions(+), 54 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 504cb17def..89e8a2b93d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3136,6 +3136,17 @@ namespace ts { return undefined; } + function getAnnotatedAccessorThisType(accessor: AccessorDeclaration): Type { + if (accessor && + accessor.parameters.length === (accessor.kind === SyntaxKind.GetAccessor ? 1 : 2) && + accessor.parameters[0].name.kind === SyntaxKind.Identifier && + (accessor.parameters[0].name as Identifier).originalKeywordKind === SyntaxKind.ThisKeyword && + accessor.parameters[0].type ) { + return getTypeFromTypeNode(accessor.parameters[0].type); + } + return undefined; + } + function getTypeOfAccessors(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.type) { @@ -4357,20 +4368,12 @@ namespace ts { function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature { const links = getNodeLinks(declaration); if (!links.resolvedSignature) { - const classType = declaration.kind === SyntaxKind.Constructor ? - getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent).symbol)) - : undefined; - const typeParameters = classType ? classType.localTypeParameters : - declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) : - getTypeParametersFromJSDocTemplate(declaration); const parameters: Symbol[] = []; let hasStringLiterals = false; let minArgumentCount = -1; let thisType: Type = undefined; let hasThisParameter: boolean; const isJSConstructSignature = isJSDocConstructSignature(declaration); - let returnType: Type = undefined; - let typePredicate: TypePredicate = undefined; // If this is a JSDoc construct signature, then skip the first parameter in the // parameter list. The first parameter represents the return type of the construct @@ -4407,48 +4410,68 @@ namespace ts { } } + // If only one accessor includes a this-type annotation, the other behaves as if it had the same type annotation + if ((declaration.kind === SyntaxKind.GetAccessor || declaration.kind === SyntaxKind.SetAccessor) && + !hasDynamicName(declaration) && + !hasThisParameter) { + const otherKind = declaration.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; + const setter = getDeclarationOfKind(declaration.symbol, otherKind); + thisType = getAnnotatedAccessorThisType(setter); + } + if (minArgumentCount < 0) { minArgumentCount = declaration.parameters.length - (hasThisParameter ? 1 : 0); } - if (isJSConstructSignature) { minArgumentCount--; - returnType = getTypeFromTypeNode(declaration.parameters[0].type); } - else if (classType) { - returnType = classType; - } - else if (declaration.type) { - returnType = getTypeFromTypeNode(declaration.type); - if (declaration.type.kind === SyntaxKind.TypePredicate) { - typePredicate = createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode); - } - } - else { - if (declaration.flags & NodeFlags.JavaScriptFile) { - const type = getReturnTypeFromJSDocComment(declaration); - if (type && type !== unknownType) { - returnType = type; - } - } - // TypeScript 1.0 spec (April 2014): - // If only one accessor includes a type annotation, the other behaves as if it had the same type annotation. - if (declaration.kind === SyntaxKind.GetAccessor && !hasDynamicName(declaration)) { - const setter = getDeclarationOfKind(declaration.symbol, SyntaxKind.SetAccessor); - returnType = getAnnotatedAccessorType(setter); - } - - if (!returnType && nodeIsMissing((declaration).body)) { - returnType = anyType; - } - } + const classType = declaration.kind === SyntaxKind.Constructor ? + getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent).symbol)) + : undefined; + const typeParameters = classType ? classType.localTypeParameters : + declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) : + getTypeParametersFromJSDocTemplate(declaration); + const returnType = getSignatureReturnTypeFromDeclaration(declaration, minArgumentCount, isJSConstructSignature, classType); + const typePredicate = declaration.type && declaration.type.kind === SyntaxKind.TypePredicate ? + createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode) : + undefined; links.resolvedSignature = createSignature(declaration, typeParameters, thisType, parameters, returnType, typePredicate, minArgumentCount, hasRestParameter(declaration), hasStringLiterals); } return links.resolvedSignature; } + function getSignatureReturnTypeFromDeclaration(declaration: SignatureDeclaration, minArgumentCount: number, isJSConstructSignature: boolean, classType: Type) { + if (isJSConstructSignature) { + return getTypeFromTypeNode(declaration.parameters[0].type); + } + else if (classType) { + return classType; + } + else if (declaration.type) { + return getTypeFromTypeNode(declaration.type); + } + + if (declaration.flags & NodeFlags.JavaScriptFile) { + const type = getReturnTypeFromJSDocComment(declaration); + if (type && type !== unknownType) { + return type; + } + } + + // TypeScript 1.0 spec (April 2014): + // If only one accessor includes a type annotation, the other behaves as if it had the same type annotation. + if (declaration.kind === SyntaxKind.GetAccessor && !hasDynamicName(declaration)) { + const setter = getDeclarationOfKind(declaration.symbol, SyntaxKind.SetAccessor); + return getAnnotatedAccessorType(setter); + } + + if (nodeIsMissing((declaration).body)) { + return anyType; + } + } + function getSignaturesOfSymbol(symbol: Symbol): Signature[] { if (!symbol) return emptyArray; const result: Signature[] = []; @@ -12571,9 +12594,6 @@ namespace ts { if (func.kind === SyntaxKind.Constructor || func.kind === SyntaxKind.ConstructSignature || func.kind === SyntaxKind.ConstructorType) { error(node, Diagnostics.A_constructor_cannot_have_a_this_parameter); } - if (func.kind === SyntaxKind.SetAccessor) { - error(node, Diagnostics.A_setter_cannot_have_a_this_parameter); - } } // Only check rest parameter type if it's not a binding pattern. Since binding patterns are @@ -12960,15 +12980,10 @@ namespace ts { error(node.name, Diagnostics.Accessors_must_both_be_abstract_or_non_abstract); } - const currentAccessorType = getAnnotatedAccessorType(node); - const otherAccessorType = getAnnotatedAccessorType(otherAccessor); // TypeScript 1.0 spec (April 2014): 4.5 // If both accessors include type annotations, the specified types must be identical. - if (currentAccessorType && otherAccessorType) { - if (!isTypeIdenticalTo(currentAccessorType, otherAccessorType)) { - error(node, Diagnostics.get_and_set_accessor_must_have_the_same_type); - } - } + checkAccessorDeclarationTypesIdentical(node, otherAccessor, getAnnotatedAccessorType, Diagnostics.get_and_set_accessor_must_have_the_same_type); + checkAccessorDeclarationTypesIdentical(node, otherAccessor, getAnnotatedAccessorThisType, Diagnostics.get_and_set_accessor_must_have_the_same_this_type); } } getTypeOfAccessors(getSymbolOfNode(node)); @@ -12981,6 +12996,14 @@ namespace ts { } } + function checkAccessorDeclarationTypesIdentical(first: AccessorDeclaration, second: AccessorDeclaration, getAnnotatedType: (a: AccessorDeclaration) => Type, message: DiagnosticMessage) { + const firstType = getAnnotatedType(first); + const secondType = getAnnotatedType(second); + if (firstType && secondType && !isTypeIdenticalTo(firstType, secondType)) { + error(first, message); + } + } + function checkAccessorDeferred(node: AccessorDeclaration) { checkSourceElement(node.body); } @@ -18075,16 +18098,16 @@ namespace ts { else if (accessor.typeParameters) { return grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_have_type_parameters); } - else if (kind === SyntaxKind.GetAccessor && accessor.parameters.length) { - return grammarErrorOnNode(accessor.name, Diagnostics.A_get_accessor_cannot_have_parameters); + else if (!doesAccessorHaveCorrectParameterCount(accessor)) { + return grammarErrorOnNode(accessor.name, + kind === SyntaxKind.GetAccessor ? + Diagnostics.A_get_accessor_cannot_have_parameters : + Diagnostics.A_set_accessor_must_have_exactly_one_parameter); } else if (kind === SyntaxKind.SetAccessor) { if (accessor.type) { return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_cannot_have_a_return_type_annotation); } - else if (accessor.parameters.length !== 1) { - return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_must_have_exactly_one_parameter); - } else { const parameter = accessor.parameters[0]; if (parameter.dotDotDotToken) { @@ -18103,6 +18126,18 @@ namespace ts { } } + /** Does the accessor have the right number of parameters? + + A get accessor has no parameters or a single `this` parameter. + A set accessor has one parameter or a `this` parameter and one more parameter */ + function doesAccessorHaveCorrectParameterCount(accessor: MethodDeclaration) { + const isGet = accessor.kind === SyntaxKind.GetAccessor; + return (accessor.parameters.length === (isGet ? 1 : 2) && + accessor.parameters[0].name.kind === SyntaxKind.Identifier && + (accessor.parameters[0].name).originalKeywordKind === SyntaxKind.ThisKeyword) || + accessor.parameters.length === (isGet ? 0 : 1); + } + function checkGrammarForNonSymbolComputedProperty(node: DeclarationName, message: DiagnosticMessage) { if (isDynamicName(node)) { return grammarErrorOnNode(node, message); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 4988e2091b..b84f5c0805 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1911,7 +1911,7 @@ "category": "Error", "code": 2681 }, - "A setter cannot have a 'this' parameter.": { + "'get' and 'set' accessor must have the same 'this' type.": { "category": "Error", "code": 2682 }, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b6c681e5f0..25e31677be 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2269,7 +2269,12 @@ namespace ts { } export function getSetAccessorTypeAnnotationNode(accessor: AccessorDeclaration): TypeNode { - return accessor && accessor.parameters.length > 0 && accessor.parameters[0].type; + if (accessor && accessor.parameters.length > 0) { + const hasThis = accessor.parameters.length === 2 && + accessor.parameters[0].name.kind === SyntaxKind.Identifier && + (accessor.parameters[0].name as Identifier).originalKeywordKind === SyntaxKind.ThisKeyword; + return accessor.parameters[hasThis ? 1 : 0].type; + } } export function getAllAccessorDeclarations(declarations: NodeArray, accessor: AccessorDeclaration) { From ff1b083ac39c7a7bfd99dd63d1f743a0984e8326 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 4 May 2016 11:03:52 -0700 Subject: [PATCH 2/3] Add tests+baselines for accessor this parameters --- .../reference/thisTypeInAccessors.js | 73 ++++++++++ .../reference/thisTypeInAccessors.symbols | 117 ++++++++++++++++ .../reference/thisTypeInAccessors.types | 129 ++++++++++++++++++ .../thisTypeInAccessorsNegative.errors.txt | 30 ++++ .../reference/thisTypeInAccessorsNegative.js | 31 +++++ .../thisTypeInFunctionsNegative.errors.txt | 51 ++++--- .../reference/thisTypeInFunctionsNegative.js | 8 -- .../types/thisType/thisTypeInAccessors.ts | 33 +++++ .../thisType/thisTypeInAccessorsNegative.ts | 20 +++ .../thisType/thisTypeInFunctionsNegative.ts | 2 - 10 files changed, 456 insertions(+), 38 deletions(-) create mode 100644 tests/baselines/reference/thisTypeInAccessors.js create mode 100644 tests/baselines/reference/thisTypeInAccessors.symbols create mode 100644 tests/baselines/reference/thisTypeInAccessors.types create mode 100644 tests/baselines/reference/thisTypeInAccessorsNegative.errors.txt create mode 100644 tests/baselines/reference/thisTypeInAccessorsNegative.js create mode 100644 tests/cases/conformance/types/thisType/thisTypeInAccessors.ts create mode 100644 tests/cases/conformance/types/thisType/thisTypeInAccessorsNegative.ts diff --git a/tests/baselines/reference/thisTypeInAccessors.js b/tests/baselines/reference/thisTypeInAccessors.js new file mode 100644 index 0000000000..f310839844 --- /dev/null +++ b/tests/baselines/reference/thisTypeInAccessors.js @@ -0,0 +1,73 @@ +//// [thisTypeInAccessors.ts] +interface Foo { + n: number; + x: number; +} + +const explicit = { + n: 12, + get x(this: Foo): number { return this.n; }, + set x(this: Foo, n: number) { this.n = n; } +} +const copiedFromGetter = { + n: 14, + get x(this: Foo): number { return this.n; }, + set x(n) { this.n = n; } +} +const copiedFromSetter = { + n: 15, + get x() { return this.n }, + set x(this: Foo, n: number) { this.n = n; } +} + +class Explicit { + n = 17; + get x(this: Foo): number { return this.n; } + set x(this: Foo, n: number) { this.n = n; } +} +class Contextual { + n = 21; + get x() { return this.n } // inside a class, so already correct +} + + +//// [thisTypeInAccessors.js] +var explicit = { + n: 12, + get x() { return this.n; }, + set x(n) { this.n = n; } +}; +var copiedFromGetter = { + n: 14, + get x() { return this.n; }, + set x(n) { this.n = n; } +}; +var copiedFromSetter = { + n: 15, + get x() { return this.n; }, + set x(n) { this.n = n; } +}; +var Explicit = (function () { + function Explicit() { + this.n = 17; + } + Object.defineProperty(Explicit.prototype, "x", { + get: function () { return this.n; }, + set: function (n) { this.n = n; }, + enumerable: true, + configurable: true + }); + return Explicit; +}()); +var Contextual = (function () { + function Contextual() { + this.n = 21; + } + Object.defineProperty(Contextual.prototype, "x", { + get: function () { return this.n; } // inside a class, so already correct + , + enumerable: true, + configurable: true + }); + return Contextual; +}()); diff --git a/tests/baselines/reference/thisTypeInAccessors.symbols b/tests/baselines/reference/thisTypeInAccessors.symbols new file mode 100644 index 0000000000..9344d4656f --- /dev/null +++ b/tests/baselines/reference/thisTypeInAccessors.symbols @@ -0,0 +1,117 @@ +=== tests/cases/conformance/types/thisType/thisTypeInAccessors.ts === +interface Foo { +>Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) + + n: number; +>n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) + + x: number; +>x : Symbol(Foo.x, Decl(thisTypeInAccessors.ts, 1, 14)) +} + +const explicit = { +>explicit : Symbol(explicit, Decl(thisTypeInAccessors.ts, 5, 5)) + + n: 12, +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 5, 18)) + + get x(this: Foo): number { return this.n; }, +>x : Symbol(x, Decl(thisTypeInAccessors.ts, 6, 10), Decl(thisTypeInAccessors.ts, 7, 48)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 7, 10)) +>Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) + + set x(this: Foo, n: number) { this.n = n; } +>x : Symbol(x, Decl(thisTypeInAccessors.ts, 6, 10), Decl(thisTypeInAccessors.ts, 7, 48)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 8, 10)) +>Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 8, 20)) +>this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 8, 20)) +} +const copiedFromGetter = { +>copiedFromGetter : Symbol(copiedFromGetter, Decl(thisTypeInAccessors.ts, 10, 5)) + + n: 14, +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 10, 26)) + + get x(this: Foo): number { return this.n; }, +>x : Symbol(x, Decl(thisTypeInAccessors.ts, 11, 10), Decl(thisTypeInAccessors.ts, 12, 48)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 12, 10)) +>Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) + + set x(n) { this.n = n; } +>x : Symbol(x, Decl(thisTypeInAccessors.ts, 11, 10), Decl(thisTypeInAccessors.ts, 12, 48)) +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 13, 10)) +>this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 13, 10)) +} +const copiedFromSetter = { +>copiedFromSetter : Symbol(copiedFromSetter, Decl(thisTypeInAccessors.ts, 15, 5)) + + n: 15, +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 15, 26)) + + get x() { return this.n }, +>x : Symbol(x, Decl(thisTypeInAccessors.ts, 16, 10), Decl(thisTypeInAccessors.ts, 17, 30)) +>this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) + + set x(this: Foo, n: number) { this.n = n; } +>x : Symbol(x, Decl(thisTypeInAccessors.ts, 16, 10), Decl(thisTypeInAccessors.ts, 17, 30)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 18, 10)) +>Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 18, 20)) +>this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 18, 20)) +} + +class Explicit { +>Explicit : Symbol(Explicit, Decl(thisTypeInAccessors.ts, 19, 1)) + + n = 17; +>n : Symbol(Explicit.n, Decl(thisTypeInAccessors.ts, 21, 16)) + + get x(this: Foo): number { return this.n; } +>x : Symbol(Explicit.x, Decl(thisTypeInAccessors.ts, 22, 11), Decl(thisTypeInAccessors.ts, 23, 47)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 23, 10)) +>Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) + + set x(this: Foo, n: number) { this.n = n; } +>x : Symbol(Explicit.x, Decl(thisTypeInAccessors.ts, 22, 11), Decl(thisTypeInAccessors.ts, 23, 47)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 24, 10)) +>Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 24, 20)) +>this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 24, 20)) +} +class Contextual { +>Contextual : Symbol(Contextual, Decl(thisTypeInAccessors.ts, 25, 1)) + + n = 21; +>n : Symbol(Contextual.n, Decl(thisTypeInAccessors.ts, 26, 18)) + + get x() { return this.n } // inside a class, so already correct +>x : Symbol(Contextual.x, Decl(thisTypeInAccessors.ts, 27, 11)) +>this.n : Symbol(Contextual.n, Decl(thisTypeInAccessors.ts, 26, 18)) +>this : Symbol(Contextual, Decl(thisTypeInAccessors.ts, 25, 1)) +>n : Symbol(Contextual.n, Decl(thisTypeInAccessors.ts, 26, 18)) +} + diff --git a/tests/baselines/reference/thisTypeInAccessors.types b/tests/baselines/reference/thisTypeInAccessors.types new file mode 100644 index 0000000000..f280b397fe --- /dev/null +++ b/tests/baselines/reference/thisTypeInAccessors.types @@ -0,0 +1,129 @@ +=== tests/cases/conformance/types/thisType/thisTypeInAccessors.ts === +interface Foo { +>Foo : Foo + + n: number; +>n : number + + x: number; +>x : number +} + +const explicit = { +>explicit : { n: number; x: number; } +>{ n: 12, get x(this: Foo): number { return this.n; }, set x(this: Foo, n: number) { this.n = n; }} : { n: number; x: number; } + + n: 12, +>n : number +>12 : number + + get x(this: Foo): number { return this.n; }, +>x : number +>this : Foo +>Foo : Foo +>this.n : number +>this : Foo +>n : number + + set x(this: Foo, n: number) { this.n = n; } +>x : number +>this : Foo +>Foo : Foo +>n : number +>this.n = n : number +>this.n : number +>this : Foo +>n : number +>n : number +} +const copiedFromGetter = { +>copiedFromGetter : { n: number; x: number; } +>{ n: 14, get x(this: Foo): number { return this.n; }, set x(n) { this.n = n; }} : { n: number; x: number; } + + n: 14, +>n : number +>14 : number + + get x(this: Foo): number { return this.n; }, +>x : number +>this : Foo +>Foo : Foo +>this.n : number +>this : Foo +>n : number + + set x(n) { this.n = n; } +>x : number +>n : number +>this.n = n : number +>this.n : number +>this : Foo +>n : number +>n : number +} +const copiedFromSetter = { +>copiedFromSetter : { n: number; x: number; } +>{ n: 15, get x() { return this.n }, set x(this: Foo, n: number) { this.n = n; }} : { n: number; x: number; } + + n: 15, +>n : number +>15 : number + + get x() { return this.n }, +>x : number +>this.n : number +>this : Foo +>n : number + + set x(this: Foo, n: number) { this.n = n; } +>x : number +>this : Foo +>Foo : Foo +>n : number +>this.n = n : number +>this.n : number +>this : Foo +>n : number +>n : number +} + +class Explicit { +>Explicit : Explicit + + n = 17; +>n : number +>17 : number + + get x(this: Foo): number { return this.n; } +>x : number +>this : Foo +>Foo : Foo +>this.n : number +>this : Foo +>n : number + + set x(this: Foo, n: number) { this.n = n; } +>x : number +>this : Foo +>Foo : Foo +>n : number +>this.n = n : number +>this.n : number +>this : Foo +>n : number +>n : number +} +class Contextual { +>Contextual : Contextual + + n = 21; +>n : number +>21 : number + + get x() { return this.n } // inside a class, so already correct +>x : number +>this.n : number +>this : this +>n : number +} + diff --git a/tests/baselines/reference/thisTypeInAccessorsNegative.errors.txt b/tests/baselines/reference/thisTypeInAccessorsNegative.errors.txt new file mode 100644 index 0000000000..5396f1f431 --- /dev/null +++ b/tests/baselines/reference/thisTypeInAccessorsNegative.errors.txt @@ -0,0 +1,30 @@ +tests/cases/conformance/types/thisType/thisTypeInAccessorsNegative.ts(10,9): error TS2682: 'get' and 'set' accessor must have the same 'this' type. +tests/cases/conformance/types/thisType/thisTypeInAccessorsNegative.ts(11,9): error TS2682: 'get' and 'set' accessor must have the same 'this' type. +tests/cases/conformance/types/thisType/thisTypeInAccessorsNegative.ts(16,22): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. + + +==== tests/cases/conformance/types/thisType/thisTypeInAccessorsNegative.ts (3 errors) ==== + interface Foo { + n: number; + x: number; + } + interface Bar { + wrong: "place" | "time" | "method" | "technique"; + } + const mismatch = { + n: 13, + get x(this: Foo) { return this.n; }, + ~ +!!! error TS2682: 'get' and 'set' accessor must have the same 'this' type. + set x(this: Bar, n) { this.wrong = "method"; } + ~ +!!! error TS2682: 'get' and 'set' accessor must have the same 'this' type. + } + const contextual: Foo = { + n: 16, + // there is no contextual this type from an Foo.x. + get x() { return this.n; } + ~~~~ +!!! error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. + } + \ No newline at end of file diff --git a/tests/baselines/reference/thisTypeInAccessorsNegative.js b/tests/baselines/reference/thisTypeInAccessorsNegative.js new file mode 100644 index 0000000000..4d0826232f --- /dev/null +++ b/tests/baselines/reference/thisTypeInAccessorsNegative.js @@ -0,0 +1,31 @@ +//// [thisTypeInAccessorsNegative.ts] +interface Foo { + n: number; + x: number; +} +interface Bar { + wrong: "place" | "time" | "method" | "technique"; +} +const mismatch = { + n: 13, + get x(this: Foo) { return this.n; }, + set x(this: Bar, n) { this.wrong = "method"; } +} +const contextual: Foo = { + n: 16, + // there is no contextual this type from an Foo.x. + get x() { return this.n; } +} + + +//// [thisTypeInAccessorsNegative.js] +var mismatch = { + n: 13, + get x() { return this.n; }, + set x(n) { this.wrong = "method"; } +}; +var contextual = { + n: 16, + // there is no contextual this type from an Foo.x. + get x() { return this.n; } +}; diff --git a/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt b/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt index 017550dcec..564aa2ed92 100644 --- a/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt +++ b/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt @@ -75,32 +75,31 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(146,1): er tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(148,1): error TS2322: Type '(this: Base2) => number' is not assignable to type '(this: Base1) => number'. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(154,16): error TS2679: A function that is called with the 'new' keyword cannot have a 'this' type that is 'void'. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(158,17): error TS2681: A constructor cannot have a 'this' parameter. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(160,11): error TS2682: A setter cannot have a 'this' parameter. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(164,9): error TS2681: A constructor cannot have a 'this' parameter. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(166,31): error TS2681: A constructor cannot have a 'this' parameter. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(167,30): error TS2680: A 'this' parameter must be the first parameter. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(170,26): error TS1003: Identifier expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(170,30): error TS1005: ',' expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(171,20): error TS2370: A rest parameter must be of an array type. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(171,23): error TS1003: Identifier expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(171,27): error TS1005: ',' expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,23): error TS1005: ',' expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,24): error TS1138: Parameter declaration expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(173,28): error TS1003: Identifier expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(173,32): error TS1005: ',' expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(174,30): error TS1005: ',' expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(174,32): error TS1138: Parameter declaration expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(174,39): error TS1005: ';' expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(174,40): error TS1128: Declaration or statement expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(174,42): error TS2304: Cannot find name 'number'. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(174,49): error TS1005: ';' expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(177,1): error TS7027: Unreachable code detected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(177,29): error TS2304: Cannot find name 'm'. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(177,32): error TS1005: ';' expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(177,35): error TS2304: Cannot find name 'm'. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(162,9): error TS2681: A constructor cannot have a 'this' parameter. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(164,31): error TS2681: A constructor cannot have a 'this' parameter. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(165,30): error TS2680: A 'this' parameter must be the first parameter. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(168,26): error TS1003: Identifier expected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(168,30): error TS1005: ',' expected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(169,20): error TS2370: A rest parameter must be of an array type. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(169,23): error TS1003: Identifier expected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(169,27): error TS1005: ',' expected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(170,23): error TS1005: ',' expected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(170,24): error TS1138: Parameter declaration expected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(171,28): error TS1003: Identifier expected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(171,32): error TS1005: ',' expected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,30): error TS1005: ',' expected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,32): error TS1138: Parameter declaration expected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,39): error TS1005: ';' expected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,40): error TS1128: Declaration or statement expected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,42): error TS2304: Cannot find name 'number'. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,49): error TS1005: ';' expected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(175,1): error TS7027: Unreachable code detected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(175,29): error TS2304: Cannot find name 'm'. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(175,32): error TS1005: ';' expected. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(175,35): error TS2304: Cannot find name 'm'. -==== tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts (66 errors) ==== +==== tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts (65 errors) ==== class C { n: number; explicitThis(this: this, m: number): number { @@ -380,10 +379,6 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(177,35): e ~~~~~~~~~~~~~~~~~~~~~ !!! error TS2681: A constructor cannot have a 'this' parameter. } - set p(this: void) { - ~~~~~~~~~~ -!!! error TS2682: A setter cannot have a 'this' parameter. - } } interface ThisConstructorInterface { new(this: ThisConstructor, n: number); diff --git a/tests/baselines/reference/thisTypeInFunctionsNegative.js b/tests/baselines/reference/thisTypeInFunctionsNegative.js index 124a7c8801..602c6afefd 100644 --- a/tests/baselines/reference/thisTypeInFunctionsNegative.js +++ b/tests/baselines/reference/thisTypeInFunctionsNegative.js @@ -158,8 +158,6 @@ let voidThis = new VoidThis(); class ThisConstructor { constructor(this: ThisConstructor, private n: number) { } - set p(this: void) { - } } interface ThisConstructorInterface { new(this: ThisConstructor, n: number); @@ -332,12 +330,6 @@ var ThisConstructor = (function () { function ThisConstructor(n) { this.n = n; } - Object.defineProperty(ThisConstructor.prototype, "p", { - set: function () { - }, - enumerable: true, - configurable: true - }); return ThisConstructor; }()); var thisConstructorType; diff --git a/tests/cases/conformance/types/thisType/thisTypeInAccessors.ts b/tests/cases/conformance/types/thisType/thisTypeInAccessors.ts new file mode 100644 index 0000000000..a59fa1ead5 --- /dev/null +++ b/tests/cases/conformance/types/thisType/thisTypeInAccessors.ts @@ -0,0 +1,33 @@ +// @noImplicitAny: true +// @noImplicitThis: true +// @target: es5 +interface Foo { + n: number; + x: number; +} + +const explicit = { + n: 12, + get x(this: Foo): number { return this.n; }, + set x(this: Foo, n: number) { this.n = n; } +} +const copiedFromGetter = { + n: 14, + get x(this: Foo): number { return this.n; }, + set x(n) { this.n = n; } +} +const copiedFromSetter = { + n: 15, + get x() { return this.n }, + set x(this: Foo, n: number) { this.n = n; } +} + +class Explicit { + n = 17; + get x(this: Foo): number { return this.n; } + set x(this: Foo, n: number) { this.n = n; } +} +class Contextual { + n = 21; + get x() { return this.n } // inside a class, so already correct +} diff --git a/tests/cases/conformance/types/thisType/thisTypeInAccessorsNegative.ts b/tests/cases/conformance/types/thisType/thisTypeInAccessorsNegative.ts new file mode 100644 index 0000000000..69ff4175a9 --- /dev/null +++ b/tests/cases/conformance/types/thisType/thisTypeInAccessorsNegative.ts @@ -0,0 +1,20 @@ +// @noImplicitAny: true +// @noImplicitThis: true +// @target: es5 +interface Foo { + n: number; + x: number; +} +interface Bar { + wrong: "place" | "time" | "method" | "technique"; +} +const mismatch = { + n: 13, + get x(this: Foo) { return this.n; }, + set x(this: Bar, n) { this.wrong = "method"; } +} +const contextual: Foo = { + n: 16, + // there is no contextual this type from an Foo.x. + get x() { return this.n; } +} diff --git a/tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts b/tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts index 597fe5a836..aa2a48d5f4 100644 --- a/tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts +++ b/tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts @@ -157,8 +157,6 @@ let voidThis = new VoidThis(); class ThisConstructor { constructor(this: ThisConstructor, private n: number) { } - set p(this: void) { - } } interface ThisConstructorInterface { new(this: ThisConstructor, n: number); From 3d3bcb4a5b8256617655fcfb2e675312a8647580 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 4 May 2016 13:35:35 -0700 Subject: [PATCH 3/3] Correctly copy annotated this getter -> setter Previously it only went the other direction. --- src/compiler/checker.ts | 40 +++++++++------ .../reference/thisTypeInAccessors.js | 10 ++++ .../reference/thisTypeInAccessors.symbols | 51 ++++++++++++++----- .../reference/thisTypeInAccessors.types | 26 ++++++++++ .../types/thisType/thisTypeInAccessors.ts | 5 ++ 5 files changed, 102 insertions(+), 30 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 89e8a2b93d..8a09c2dcfd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2920,7 +2920,12 @@ namespace ts { if (func.kind === SyntaxKind.SetAccessor && !hasDynamicName(func)) { const getter = getDeclarationOfKind(declaration.parent.symbol, SyntaxKind.GetAccessor); if (getter) { - return getReturnTypeOfSignature(getSignatureFromDeclaration(getter)); + const signature = getSignatureFromDeclaration(getter); + const thisParameter = getAccessorThisParameter(func as AccessorDeclaration); + if (thisParameter && declaration === thisParameter) { + return signature.thisType; + } + return getReturnTypeOfSignature(signature); } } // Use contextual parameter type if one is available @@ -3137,12 +3142,11 @@ namespace ts { } function getAnnotatedAccessorThisType(accessor: AccessorDeclaration): Type { - if (accessor && - accessor.parameters.length === (accessor.kind === SyntaxKind.GetAccessor ? 1 : 2) && - accessor.parameters[0].name.kind === SyntaxKind.Identifier && - (accessor.parameters[0].name as Identifier).originalKeywordKind === SyntaxKind.ThisKeyword && - accessor.parameters[0].type ) { - return getTypeFromTypeNode(accessor.parameters[0].type); + if (accessor) { + const parameter = getAccessorThisParameter(accessor); + if (parameter && parameter.type) { + return getTypeFromTypeNode(accessor.parameters[0].type); + } } return undefined; } @@ -4413,7 +4417,7 @@ namespace ts { // If only one accessor includes a this-type annotation, the other behaves as if it had the same type annotation if ((declaration.kind === SyntaxKind.GetAccessor || declaration.kind === SyntaxKind.SetAccessor) && !hasDynamicName(declaration) && - !hasThisParameter) { + (!hasThisParameter || thisType === unknownType)) { const otherKind = declaration.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; const setter = getDeclarationOfKind(declaration.symbol, otherKind); thisType = getAnnotatedAccessorThisType(setter); @@ -18084,7 +18088,7 @@ namespace ts { return false; } - function checkGrammarAccessor(accessor: MethodDeclaration): boolean { + function checkGrammarAccessor(accessor: AccessorDeclaration): boolean { const kind = accessor.kind; if (languageVersion < ScriptTarget.ES5) { return grammarErrorOnNode(accessor.name, Diagnostics.Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher); @@ -18130,13 +18134,17 @@ namespace ts { A get accessor has no parameters or a single `this` parameter. A set accessor has one parameter or a `this` parameter and one more parameter */ - function doesAccessorHaveCorrectParameterCount(accessor: MethodDeclaration) { - const isGet = accessor.kind === SyntaxKind.GetAccessor; - return (accessor.parameters.length === (isGet ? 1 : 2) && - accessor.parameters[0].name.kind === SyntaxKind.Identifier && - (accessor.parameters[0].name).originalKeywordKind === SyntaxKind.ThisKeyword) || - accessor.parameters.length === (isGet ? 0 : 1); - } + function doesAccessorHaveCorrectParameterCount(accessor: AccessorDeclaration) { + return getAccessorThisParameter(accessor) || accessor.parameters.length === (accessor.kind === SyntaxKind.GetAccessor ? 0 : 1); + } + + function getAccessorThisParameter(accessor: AccessorDeclaration) { + if (accessor.parameters.length === (accessor.kind === SyntaxKind.GetAccessor ? 1 : 2) && + accessor.parameters[0].name.kind === SyntaxKind.Identifier && + (accessor.parameters[0].name).originalKeywordKind === SyntaxKind.ThisKeyword) { + return accessor.parameters[0]; + } + } function checkGrammarForNonSymbolComputedProperty(node: DeclarationName, message: DiagnosticMessage) { if (isDynamicName(node)) { diff --git a/tests/baselines/reference/thisTypeInAccessors.js b/tests/baselines/reference/thisTypeInAccessors.js index f310839844..ed339d0366 100644 --- a/tests/baselines/reference/thisTypeInAccessors.js +++ b/tests/baselines/reference/thisTypeInAccessors.js @@ -19,6 +19,11 @@ const copiedFromSetter = { get x() { return this.n }, set x(this: Foo, n: number) { this.n = n; } } +const copiedFromGetterUnannotated = { + n: 16, + get x(this: Foo) { return this.n }, + set x(this, n) { this.n = n; } +} class Explicit { n = 17; @@ -47,6 +52,11 @@ var copiedFromSetter = { get x() { return this.n; }, set x(n) { this.n = n; } }; +var copiedFromGetterUnannotated = { + n: 16, + get x() { return this.n; }, + set x(n) { this.n = n; } +}; var Explicit = (function () { function Explicit() { this.n = 17; diff --git a/tests/baselines/reference/thisTypeInAccessors.symbols b/tests/baselines/reference/thisTypeInAccessors.symbols index 9344d4656f..ef2201d380 100644 --- a/tests/baselines/reference/thisTypeInAccessors.symbols +++ b/tests/baselines/reference/thisTypeInAccessors.symbols @@ -77,41 +77,64 @@ const copiedFromSetter = { >n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) >n : Symbol(n, Decl(thisTypeInAccessors.ts, 18, 20)) } +const copiedFromGetterUnannotated = { +>copiedFromGetterUnannotated : Symbol(copiedFromGetterUnannotated, Decl(thisTypeInAccessors.ts, 20, 5)) + + n: 16, +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 20, 37)) + + get x(this: Foo) { return this.n }, +>x : Symbol(x, Decl(thisTypeInAccessors.ts, 21, 10), Decl(thisTypeInAccessors.ts, 22, 39)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 22, 10)) +>Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) + + set x(this, n) { this.n = n; } +>x : Symbol(x, Decl(thisTypeInAccessors.ts, 21, 10), Decl(thisTypeInAccessors.ts, 22, 39)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 23, 10)) +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 23, 15)) +>this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 23, 15)) +} class Explicit { ->Explicit : Symbol(Explicit, Decl(thisTypeInAccessors.ts, 19, 1)) +>Explicit : Symbol(Explicit, Decl(thisTypeInAccessors.ts, 24, 1)) n = 17; ->n : Symbol(Explicit.n, Decl(thisTypeInAccessors.ts, 21, 16)) +>n : Symbol(Explicit.n, Decl(thisTypeInAccessors.ts, 26, 16)) get x(this: Foo): number { return this.n; } ->x : Symbol(Explicit.x, Decl(thisTypeInAccessors.ts, 22, 11), Decl(thisTypeInAccessors.ts, 23, 47)) ->this : Symbol(this, Decl(thisTypeInAccessors.ts, 23, 10)) +>x : Symbol(Explicit.x, Decl(thisTypeInAccessors.ts, 27, 11), Decl(thisTypeInAccessors.ts, 28, 47)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 28, 10)) >Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) >this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) >this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) >n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) set x(this: Foo, n: number) { this.n = n; } ->x : Symbol(Explicit.x, Decl(thisTypeInAccessors.ts, 22, 11), Decl(thisTypeInAccessors.ts, 23, 47)) ->this : Symbol(this, Decl(thisTypeInAccessors.ts, 24, 10)) +>x : Symbol(Explicit.x, Decl(thisTypeInAccessors.ts, 27, 11), Decl(thisTypeInAccessors.ts, 28, 47)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 29, 10)) >Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) ->n : Symbol(n, Decl(thisTypeInAccessors.ts, 24, 20)) +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 29, 20)) >this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) >this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) >n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) ->n : Symbol(n, Decl(thisTypeInAccessors.ts, 24, 20)) +>n : Symbol(n, Decl(thisTypeInAccessors.ts, 29, 20)) } class Contextual { ->Contextual : Symbol(Contextual, Decl(thisTypeInAccessors.ts, 25, 1)) +>Contextual : Symbol(Contextual, Decl(thisTypeInAccessors.ts, 30, 1)) n = 21; ->n : Symbol(Contextual.n, Decl(thisTypeInAccessors.ts, 26, 18)) +>n : Symbol(Contextual.n, Decl(thisTypeInAccessors.ts, 31, 18)) get x() { return this.n } // inside a class, so already correct ->x : Symbol(Contextual.x, Decl(thisTypeInAccessors.ts, 27, 11)) ->this.n : Symbol(Contextual.n, Decl(thisTypeInAccessors.ts, 26, 18)) ->this : Symbol(Contextual, Decl(thisTypeInAccessors.ts, 25, 1)) ->n : Symbol(Contextual.n, Decl(thisTypeInAccessors.ts, 26, 18)) +>x : Symbol(Contextual.x, Decl(thisTypeInAccessors.ts, 32, 11)) +>this.n : Symbol(Contextual.n, Decl(thisTypeInAccessors.ts, 31, 18)) +>this : Symbol(Contextual, Decl(thisTypeInAccessors.ts, 30, 1)) +>n : Symbol(Contextual.n, Decl(thisTypeInAccessors.ts, 31, 18)) } diff --git a/tests/baselines/reference/thisTypeInAccessors.types b/tests/baselines/reference/thisTypeInAccessors.types index f280b397fe..998658c5d5 100644 --- a/tests/baselines/reference/thisTypeInAccessors.types +++ b/tests/baselines/reference/thisTypeInAccessors.types @@ -86,6 +86,32 @@ const copiedFromSetter = { >n : number >n : number } +const copiedFromGetterUnannotated = { +>copiedFromGetterUnannotated : { n: number; x: number; } +>{ n: 16, get x(this: Foo) { return this.n }, set x(this, n) { this.n = n; }} : { n: number; x: number; } + + n: 16, +>n : number +>16 : number + + get x(this: Foo) { return this.n }, +>x : number +>this : Foo +>Foo : Foo +>this.n : number +>this : Foo +>n : number + + set x(this, n) { this.n = n; } +>x : number +>this : Foo +>n : number +>this.n = n : number +>this.n : number +>this : Foo +>n : number +>n : number +} class Explicit { >Explicit : Explicit diff --git a/tests/cases/conformance/types/thisType/thisTypeInAccessors.ts b/tests/cases/conformance/types/thisType/thisTypeInAccessors.ts index a59fa1ead5..b090375976 100644 --- a/tests/cases/conformance/types/thisType/thisTypeInAccessors.ts +++ b/tests/cases/conformance/types/thisType/thisTypeInAccessors.ts @@ -21,6 +21,11 @@ const copiedFromSetter = { get x() { return this.n }, set x(this: Foo, n: number) { this.n = n; } } +const copiedFromGetterUnannotated = { + n: 16, + get x(this: Foo) { return this.n }, + set x(this, n) { this.n = n; } +} class Explicit { n = 17;