Skip to content

Commit 2c70051

Browse files
committed
Allow this parameters for accessors
Also refactor getSignatureFromDeclaration a bit
1 parent e9122a9 commit 2c70051

3 files changed

Lines changed: 94 additions & 54 deletions

File tree

src/compiler/checker.ts

Lines changed: 87 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3136,6 +3136,17 @@ namespace ts {
31363136
return undefined;
31373137
}
31383138

3139+
function getAnnotatedAccessorThisType(accessor: AccessorDeclaration): Type {
3140+
if (accessor &&
3141+
accessor.parameters.length === (accessor.kind === SyntaxKind.GetAccessor ? 1 : 2) &&
3142+
accessor.parameters[0].name.kind === SyntaxKind.Identifier &&
3143+
(accessor.parameters[0].name as Identifier).originalKeywordKind === SyntaxKind.ThisKeyword &&
3144+
accessor.parameters[0].type ) {
3145+
return getTypeFromTypeNode(accessor.parameters[0].type);
3146+
}
3147+
return undefined;
3148+
}
3149+
31393150
function getTypeOfAccessors(symbol: Symbol): Type {
31403151
const links = getSymbolLinks(symbol);
31413152
if (!links.type) {
@@ -4357,20 +4368,12 @@ namespace ts {
43574368
function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature {
43584369
const links = getNodeLinks(declaration);
43594370
if (!links.resolvedSignature) {
4360-
const classType = declaration.kind === SyntaxKind.Constructor ?
4361-
getDeclaredTypeOfClassOrInterface(getMergedSymbol((<ClassDeclaration>declaration.parent).symbol))
4362-
: undefined;
4363-
const typeParameters = classType ? classType.localTypeParameters :
4364-
declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) :
4365-
getTypeParametersFromJSDocTemplate(declaration);
43664371
const parameters: Symbol[] = [];
43674372
let hasStringLiterals = false;
43684373
let minArgumentCount = -1;
43694374
let thisType: Type = undefined;
43704375
let hasThisParameter: boolean;
43714376
const isJSConstructSignature = isJSDocConstructSignature(declaration);
4372-
let returnType: Type = undefined;
4373-
let typePredicate: TypePredicate = undefined;
43744377

43754378
// If this is a JSDoc construct signature, then skip the first parameter in the
43764379
// parameter list. The first parameter represents the return type of the construct
@@ -4407,48 +4410,68 @@ namespace ts {
44074410
}
44084411
}
44094412

4413+
// If only one accessor includes a this-type annotation, the other behaves as if it had the same type annotation
4414+
if ((declaration.kind === SyntaxKind.GetAccessor || declaration.kind === SyntaxKind.SetAccessor) &&
4415+
!hasDynamicName(declaration) &&
4416+
!hasThisParameter) {
4417+
const otherKind = declaration.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor;
4418+
const setter = <AccessorDeclaration>getDeclarationOfKind(declaration.symbol, otherKind);
4419+
thisType = getAnnotatedAccessorThisType(setter);
4420+
}
4421+
44104422
if (minArgumentCount < 0) {
44114423
minArgumentCount = declaration.parameters.length - (hasThisParameter ? 1 : 0);
44124424
}
4413-
44144425
if (isJSConstructSignature) {
44154426
minArgumentCount--;
4416-
returnType = getTypeFromTypeNode(declaration.parameters[0].type);
4417-
}
4418-
else if (classType) {
4419-
returnType = classType;
44204427
}
4421-
else if (declaration.type) {
4422-
returnType = getTypeFromTypeNode(declaration.type);
4423-
if (declaration.type.kind === SyntaxKind.TypePredicate) {
4424-
typePredicate = createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode);
4425-
}
4426-
}
4427-
else {
4428-
if (declaration.flags & NodeFlags.JavaScriptFile) {
4429-
const type = getReturnTypeFromJSDocComment(declaration);
4430-
if (type && type !== unknownType) {
4431-
returnType = type;
4432-
}
4433-
}
4434-
4435-
// TypeScript 1.0 spec (April 2014):
4436-
// If only one accessor includes a type annotation, the other behaves as if it had the same type annotation.
4437-
if (declaration.kind === SyntaxKind.GetAccessor && !hasDynamicName(declaration)) {
4438-
const setter = <AccessorDeclaration>getDeclarationOfKind(declaration.symbol, SyntaxKind.SetAccessor);
4439-
returnType = getAnnotatedAccessorType(setter);
4440-
}
44414428

4442-
if (!returnType && nodeIsMissing((<FunctionLikeDeclaration>declaration).body)) {
4443-
returnType = anyType;
4444-
}
4445-
}
4429+
const classType = declaration.kind === SyntaxKind.Constructor ?
4430+
getDeclaredTypeOfClassOrInterface(getMergedSymbol((<ClassDeclaration>declaration.parent).symbol))
4431+
: undefined;
4432+
const typeParameters = classType ? classType.localTypeParameters :
4433+
declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) :
4434+
getTypeParametersFromJSDocTemplate(declaration);
4435+
const returnType = getSignatureReturnTypeFromDeclaration(declaration, minArgumentCount, isJSConstructSignature, classType);
4436+
const typePredicate = declaration.type && declaration.type.kind === SyntaxKind.TypePredicate ?
4437+
createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode) :
4438+
undefined;
44464439

44474440
links.resolvedSignature = createSignature(declaration, typeParameters, thisType, parameters, returnType, typePredicate, minArgumentCount, hasRestParameter(declaration), hasStringLiterals);
44484441
}
44494442
return links.resolvedSignature;
44504443
}
44514444

4445+
function getSignatureReturnTypeFromDeclaration(declaration: SignatureDeclaration, minArgumentCount: number, isJSConstructSignature: boolean, classType: Type) {
4446+
if (isJSConstructSignature) {
4447+
return getTypeFromTypeNode(declaration.parameters[0].type);
4448+
}
4449+
else if (classType) {
4450+
return classType;
4451+
}
4452+
else if (declaration.type) {
4453+
return getTypeFromTypeNode(declaration.type);
4454+
}
4455+
4456+
if (declaration.flags & NodeFlags.JavaScriptFile) {
4457+
const type = getReturnTypeFromJSDocComment(declaration);
4458+
if (type && type !== unknownType) {
4459+
return type;
4460+
}
4461+
}
4462+
4463+
// TypeScript 1.0 spec (April 2014):
4464+
// If only one accessor includes a type annotation, the other behaves as if it had the same type annotation.
4465+
if (declaration.kind === SyntaxKind.GetAccessor && !hasDynamicName(declaration)) {
4466+
const setter = <AccessorDeclaration>getDeclarationOfKind(declaration.symbol, SyntaxKind.SetAccessor);
4467+
return getAnnotatedAccessorType(setter);
4468+
}
4469+
4470+
if (nodeIsMissing((<FunctionLikeDeclaration>declaration).body)) {
4471+
return anyType;
4472+
}
4473+
}
4474+
44524475
function getSignaturesOfSymbol(symbol: Symbol): Signature[] {
44534476
if (!symbol) return emptyArray;
44544477
const result: Signature[] = [];
@@ -12571,9 +12594,6 @@ namespace ts {
1257112594
if (func.kind === SyntaxKind.Constructor || func.kind === SyntaxKind.ConstructSignature || func.kind === SyntaxKind.ConstructorType) {
1257212595
error(node, Diagnostics.A_constructor_cannot_have_a_this_parameter);
1257312596
}
12574-
if (func.kind === SyntaxKind.SetAccessor) {
12575-
error(node, Diagnostics.A_setter_cannot_have_a_this_parameter);
12576-
}
1257712597
}
1257812598

1257912599
// Only check rest parameter type if it's not a binding pattern. Since binding patterns are
@@ -12960,15 +12980,10 @@ namespace ts {
1296012980
error(node.name, Diagnostics.Accessors_must_both_be_abstract_or_non_abstract);
1296112981
}
1296212982

12963-
const currentAccessorType = getAnnotatedAccessorType(node);
12964-
const otherAccessorType = getAnnotatedAccessorType(otherAccessor);
1296512983
// TypeScript 1.0 spec (April 2014): 4.5
1296612984
// If both accessors include type annotations, the specified types must be identical.
12967-
if (currentAccessorType && otherAccessorType) {
12968-
if (!isTypeIdenticalTo(currentAccessorType, otherAccessorType)) {
12969-
error(node, Diagnostics.get_and_set_accessor_must_have_the_same_type);
12970-
}
12971-
}
12985+
checkAccessorDeclarationTypesIdentical(node, otherAccessor, getAnnotatedAccessorType, Diagnostics.get_and_set_accessor_must_have_the_same_type);
12986+
checkAccessorDeclarationTypesIdentical(node, otherAccessor, getAnnotatedAccessorThisType, Diagnostics.get_and_set_accessor_must_have_the_same_this_type);
1297212987
}
1297312988
}
1297412989
getTypeOfAccessors(getSymbolOfNode(node));
@@ -12981,6 +12996,14 @@ namespace ts {
1298112996
}
1298212997
}
1298312998

12999+
function checkAccessorDeclarationTypesIdentical(first: AccessorDeclaration, second: AccessorDeclaration, getAnnotatedType: (a: AccessorDeclaration) => Type, message: DiagnosticMessage) {
13000+
const firstType = getAnnotatedType(first);
13001+
const secondType = getAnnotatedType(second);
13002+
if (firstType && secondType && !isTypeIdenticalTo(firstType, secondType)) {
13003+
error(first, message);
13004+
}
13005+
}
13006+
1298413007
function checkAccessorDeferred(node: AccessorDeclaration) {
1298513008
checkSourceElement(node.body);
1298613009
}
@@ -18075,16 +18098,16 @@ namespace ts {
1807518098
else if (accessor.typeParameters) {
1807618099
return grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_have_type_parameters);
1807718100
}
18078-
else if (kind === SyntaxKind.GetAccessor && accessor.parameters.length) {
18079-
return grammarErrorOnNode(accessor.name, Diagnostics.A_get_accessor_cannot_have_parameters);
18101+
else if (!doesAccessorHaveCorrectParameterCount(accessor)) {
18102+
return grammarErrorOnNode(accessor.name,
18103+
kind === SyntaxKind.GetAccessor ?
18104+
Diagnostics.A_get_accessor_cannot_have_parameters :
18105+
Diagnostics.A_set_accessor_must_have_exactly_one_parameter);
1808018106
}
1808118107
else if (kind === SyntaxKind.SetAccessor) {
1808218108
if (accessor.type) {
1808318109
return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_cannot_have_a_return_type_annotation);
1808418110
}
18085-
else if (accessor.parameters.length !== 1) {
18086-
return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_must_have_exactly_one_parameter);
18087-
}
1808818111
else {
1808918112
const parameter = accessor.parameters[0];
1809018113
if (parameter.dotDotDotToken) {
@@ -18103,6 +18126,18 @@ namespace ts {
1810318126
}
1810418127
}
1810518128

18129+
/** Does the accessor have the right number of parameters?
18130+
18131+
A get accessor has no parameters or a single `this` parameter.
18132+
A set accessor has one parameter or a `this` parameter and one more parameter */
18133+
function doesAccessorHaveCorrectParameterCount(accessor: MethodDeclaration) {
18134+
const isGet = accessor.kind === SyntaxKind.GetAccessor;
18135+
return (accessor.parameters.length === (isGet ? 1 : 2) &&
18136+
accessor.parameters[0].name.kind === SyntaxKind.Identifier &&
18137+
(<Identifier>accessor.parameters[0].name).originalKeywordKind === SyntaxKind.ThisKeyword) ||
18138+
accessor.parameters.length === (isGet ? 0 : 1);
18139+
}
18140+
1810618141
function checkGrammarForNonSymbolComputedProperty(node: DeclarationName, message: DiagnosticMessage) {
1810718142
if (isDynamicName(node)) {
1810818143
return grammarErrorOnNode(node, message);

src/compiler/diagnosticMessages.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1911,7 +1911,7 @@
19111911
"category": "Error",
19121912
"code": 2681
19131913
},
1914-
"A setter cannot have a 'this' parameter.": {
1914+
"'get' and 'set' accessor must have the same 'this' type.": {
19151915
"category": "Error",
19161916
"code": 2682
19171917
},

src/compiler/utilities.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2269,7 +2269,12 @@ namespace ts {
22692269
}
22702270

22712271
export function getSetAccessorTypeAnnotationNode(accessor: AccessorDeclaration): TypeNode {
2272-
return accessor && accessor.parameters.length > 0 && accessor.parameters[0].type;
2272+
if (accessor && accessor.parameters.length > 0) {
2273+
const hasThis = accessor.parameters.length === 2 &&
2274+
accessor.parameters[0].name.kind === SyntaxKind.Identifier &&
2275+
(accessor.parameters[0].name as Identifier).originalKeywordKind === SyntaxKind.ThisKeyword;
2276+
return accessor.parameters[hasThis ? 1 : 0].type;
2277+
}
22732278
}
22742279

22752280
export function getAllAccessorDeclarations(declarations: NodeArray<Declaration>, accessor: AccessorDeclaration) {

0 commit comments

Comments
 (0)