Skip to content

Commit a4cabe7

Browse files
authored
Support for auto-accessor fields from the Stage 3 Decorators proposal (#49705)
* Support for auto-accessor fields * Add tests, ensure accessors are initialized in ctor * classFields cleanup and PR feedback
1 parent 7737473 commit a4cabe7

File tree

196 files changed

+7457
-1227
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

196 files changed

+7457
-1227
lines changed

scripts/eslint/rules/debug-assert.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ module.exports = createRule({
4646
context.report({ messageId: "secondArgumentDebugAssertError", node: message1Node });
4747
}
4848

49-
if (argsLen < 3) {
49+
if (argsLen !== 3) {
5050
return;
5151
}
5252

src/compiler/binder.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2732,7 +2732,10 @@ namespace ts {
27322732
}
27332733

27342734
function bindPropertyWorker(node: PropertyDeclaration | PropertySignature) {
2735-
return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
2735+
const isAutoAccessor = isAutoAccessorPropertyDeclaration(node);
2736+
const includes = isAutoAccessor ? SymbolFlags.Accessor : SymbolFlags.Property;
2737+
const excludes = isAutoAccessor ? SymbolFlags.AccessorExcludes : SymbolFlags.PropertyExcludes;
2738+
return bindPropertyOrMethodOrAccessor(node, includes | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), excludes);
27362739
}
27372740

27382741
function bindAnonymousTypeWorker(node: TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral) {

src/compiler/checker.ts

Lines changed: 74 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9083,7 +9083,7 @@ namespace ts {
90839083
return getTypeForBindingElement(declaration as BindingElement);
90849084
}
90859085

9086-
const isProperty = isPropertyDeclaration(declaration) || isPropertySignature(declaration);
9086+
const isProperty = isPropertyDeclaration(declaration) && !hasAccessorModifier(declaration) || isPropertySignature(declaration);
90879087
const isOptional = includeOptionality && (
90889088
isProperty && !!declaration.questionToken ||
90899089
isParameter(declaration) && (!!declaration.questionToken || isJSDocOptionalParameter(declaration)) ||
@@ -9831,21 +9831,25 @@ namespace ts {
98319831
return type;
98329832
}
98339833

9834-
function getAnnotatedAccessorTypeNode(accessor: AccessorDeclaration | undefined): TypeNode | undefined {
9834+
function getAnnotatedAccessorTypeNode(accessor: AccessorDeclaration | PropertyDeclaration | undefined): TypeNode | undefined {
98359835
if (accessor) {
9836-
if (accessor.kind === SyntaxKind.GetAccessor) {
9837-
const getterTypeAnnotation = getEffectiveReturnTypeNode(accessor);
9838-
return getterTypeAnnotation;
9839-
}
9840-
else {
9841-
const setterTypeAnnotation = getEffectiveSetAccessorTypeAnnotationNode(accessor);
9842-
return setterTypeAnnotation;
9836+
switch (accessor.kind) {
9837+
case SyntaxKind.GetAccessor:
9838+
const getterTypeAnnotation = getEffectiveReturnTypeNode(accessor);
9839+
return getterTypeAnnotation;
9840+
case SyntaxKind.SetAccessor:
9841+
const setterTypeAnnotation = getEffectiveSetAccessorTypeAnnotationNode(accessor);
9842+
return setterTypeAnnotation;
9843+
case SyntaxKind.PropertyDeclaration:
9844+
Debug.assert(hasAccessorModifier(accessor));
9845+
const accessorTypeAnnotation = getEffectiveTypeAnnotationNode(accessor);
9846+
return accessorTypeAnnotation;
98439847
}
98449848
}
98459849
return undefined;
98469850
}
98479851

9848-
function getAnnotatedAccessorType(accessor: AccessorDeclaration | undefined): Type | undefined {
9852+
function getAnnotatedAccessorType(accessor: AccessorDeclaration | PropertyDeclaration | undefined): Type | undefined {
98499853
const node = getAnnotatedAccessorTypeNode(accessor);
98509854
return node && getTypeFromTypeNode(node);
98519855
}
@@ -9867,19 +9871,26 @@ namespace ts {
98679871
}
98689872
const getter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.GetAccessor);
98699873
const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor);
9874+
const accessor = tryCast(getDeclarationOfKind<PropertyDeclaration>(symbol, SyntaxKind.PropertyDeclaration), isAutoAccessorPropertyDeclaration);
9875+
98709876
// We try to resolve a getter type annotation, a setter type annotation, or a getter function
98719877
// body return type inference, in that order.
98729878
let type = getter && isInJSFile(getter) && getTypeForDeclarationFromJSDocComment(getter) ||
98739879
getAnnotatedAccessorType(getter) ||
98749880
getAnnotatedAccessorType(setter) ||
9875-
getter && getter.body && getReturnTypeFromBody(getter);
9881+
getAnnotatedAccessorType(accessor) ||
9882+
getter && getter.body && getReturnTypeFromBody(getter) ||
9883+
accessor && accessor.initializer && getWidenedTypeForVariableLikeDeclaration(accessor, /*includeOptionality*/ true);
98769884
if (!type) {
98779885
if (setter && !isPrivateWithinAmbient(setter)) {
98789886
errorOrSuggestion(noImplicitAny, setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol));
98799887
}
98809888
else if (getter && !isPrivateWithinAmbient(getter)) {
98819889
errorOrSuggestion(noImplicitAny, getter, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol));
98829890
}
9891+
else if (accessor && !isPrivateWithinAmbient(accessor)) {
9892+
errorOrSuggestion(noImplicitAny, accessor, Diagnostics.Member_0_implicitly_has_an_1_type, symbolToString(symbol), "any");
9893+
}
98839894
type = anyType;
98849895
}
98859896
if (!popTypeResolution()) {
@@ -9889,6 +9900,9 @@ namespace ts {
98899900
else if (getAnnotatedAccessorTypeNode(setter)) {
98909901
error(setter, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol));
98919902
}
9903+
else if (getAnnotatedAccessorTypeNode(accessor)) {
9904+
error(setter, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol));
9905+
}
98929906
else if (getter && noImplicitAny) {
98939907
error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol));
98949908
}
@@ -9905,7 +9919,9 @@ namespace ts {
99059919
if (!pushTypeResolution(symbol, TypeSystemPropertyName.WriteType)) {
99069920
return errorType;
99079921
}
9908-
const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor);
9922+
9923+
const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor)
9924+
?? tryCast(getDeclarationOfKind<PropertyDeclaration>(symbol, SyntaxKind.PropertyDeclaration), isAutoAccessorPropertyDeclaration);
99099925
let writeType = getAnnotatedAccessorType(setter);
99109926
if (!popTypeResolution()) {
99119927
if (getAnnotatedAccessorTypeNode(setter)) {
@@ -11063,8 +11079,10 @@ namespace ts {
1106311079
const members = getMembersOfDeclaration(decl);
1106411080
if (members) {
1106511081
for (const member of members) {
11066-
if (isStatic === hasStaticModifier(member) && hasLateBindableName(member)) {
11067-
lateBindMember(symbol, earlySymbols, lateSymbols, member);
11082+
if (isStatic === hasStaticModifier(member)) {
11083+
if (hasLateBindableName(member)) {
11084+
lateBindMember(symbol, earlySymbols, lateSymbols, member);
11085+
}
1106811086
}
1106911087
}
1107011088
}
@@ -11078,8 +11096,10 @@ namespace ts {
1107811096
|| isBinaryExpression(member) && isPossiblyAliasedThisProperty(member, assignmentKind)
1107911097
|| assignmentKind === AssignmentDeclarationKind.ObjectDefinePrototypeProperty
1108011098
|| assignmentKind === AssignmentDeclarationKind.Prototype; // A straight `Prototype` assignment probably can never have a computed name
11081-
if (isStatic === !isInstanceMember && hasLateBindableName(member)) {
11082-
lateBindMember(symbol, earlySymbols, lateSymbols, member);
11099+
if (isStatic === !isInstanceMember) {
11100+
if (hasLateBindableName(member)) {
11101+
lateBindMember(symbol, earlySymbols, lateSymbols, member);
11102+
}
1108311103
}
1108411104
}
1108511105
}
@@ -12924,7 +12944,7 @@ namespace ts {
1292412944
}
1292512945

1292612946
function isOptionalPropertyDeclaration(node: Declaration) {
12927-
return isPropertyDeclaration(node) && node.questionToken;
12947+
return isPropertyDeclaration(node) && !hasAccessorModifier(node) && node.questionToken;
1292812948
}
1292912949

1293012950
function isOptionalJSDocPropertyLikeTag(node: Node): node is JSDocPropertyLikeTag {
@@ -30759,7 +30779,7 @@ namespace ts {
3075930779
// A method or accessor declaration decorator will have two or three arguments (see
3076030780
// `PropertyDecorator` and `MethodDecorator` in core.d.ts). If we are emitting decorators
3076130781
// for ES3, we will only pass two arguments.
30762-
const hasPropDesc = parent.kind !== SyntaxKind.PropertyDeclaration && languageVersion !== ScriptTarget.ES3;
30782+
const hasPropDesc = languageVersion !== ScriptTarget.ES3 && (!isPropertyDeclaration(parent) || hasAccessorModifier(parent));
3076330783
return [
3076430784
createSyntheticExpression(expr, getParentTypeOfClassElement(parent as ClassElement)),
3076530785
createSyntheticExpression(expr, getClassElementPropertyKeyType(parent as ClassElement)),
@@ -30778,7 +30798,7 @@ namespace ts {
3077830798
case SyntaxKind.ClassExpression:
3077930799
return 1;
3078030800
case SyntaxKind.PropertyDeclaration:
30781-
return 2;
30801+
return hasAccessorModifier(node.parent) ? 3 : 2;
3078230802
case SyntaxKind.MethodDeclaration:
3078330803
case SyntaxKind.GetAccessor:
3078430804
case SyntaxKind.SetAccessor:
@@ -44189,6 +44209,9 @@ namespace ts {
4418944209
else if (flags & ModifierFlags.Readonly) {
4419044210
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "override", "readonly");
4419144211
}
44212+
else if (flags & ModifierFlags.Accessor) {
44213+
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "override", "accessor");
44214+
}
4419244215
else if (flags & ModifierFlags.Async) {
4419344216
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "override", "async");
4419444217
}
@@ -44210,6 +44233,9 @@ namespace ts {
4421044233
else if (flags & ModifierFlags.Static) {
4421144234
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "static");
4421244235
}
44236+
else if (flags & ModifierFlags.Accessor) {
44237+
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "accessor");
44238+
}
4421344239
else if (flags & ModifierFlags.Readonly) {
4421444240
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "readonly");
4421544241
}
@@ -44243,6 +44269,9 @@ namespace ts {
4424344269
else if (flags & ModifierFlags.Async) {
4424444270
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "async");
4424544271
}
44272+
else if (flags & ModifierFlags.Accessor) {
44273+
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "accessor");
44274+
}
4424644275
else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) {
4424744276
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_or_namespace_element, "static");
4424844277
}
@@ -44259,6 +44288,23 @@ namespace ts {
4425944288
lastStatic = modifier;
4426044289
break;
4426144290

44291+
case SyntaxKind.AccessorKeyword:
44292+
if (flags & ModifierFlags.Accessor) {
44293+
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "accessor");
44294+
}
44295+
else if (flags & ModifierFlags.Readonly) {
44296+
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "accessor", "readonly");
44297+
}
44298+
else if (flags & ModifierFlags.Ambient) {
44299+
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "accessor", "declare");
44300+
}
44301+
else if (node.kind !== SyntaxKind.PropertyDeclaration) {
44302+
return grammarErrorOnNode(modifier, Diagnostics.accessor_modifier_can_only_appear_on_a_property_declaration);
44303+
}
44304+
44305+
flags |= ModifierFlags.Accessor;
44306+
break;
44307+
4426244308
case SyntaxKind.ReadonlyKeyword:
4426344309
if (flags & ModifierFlags.Readonly) {
4426444310
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "readonly");
@@ -44355,6 +44401,9 @@ namespace ts {
4435544401
if (flags & ModifierFlags.Override) {
4435644402
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "abstract", "override");
4435744403
}
44404+
if (flags & ModifierFlags.Accessor) {
44405+
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "abstract", "accessor");
44406+
}
4435844407
}
4435944408
if (isNamedDeclaration(node) && node.name.kind === SyntaxKind.PrivateIdentifier) {
4436044409
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_a_private_identifier, "abstract");
@@ -45553,6 +45602,12 @@ namespace ts {
4555345602
if (languageVersion < ScriptTarget.ES2015 && isPrivateIdentifier(node.name)) {
4555445603
return grammarErrorOnNode(node.name, Diagnostics.Private_identifiers_are_only_available_when_targeting_ECMAScript_2015_and_higher);
4555545604
}
45605+
if (languageVersion < ScriptTarget.ES2015 && isAutoAccessorPropertyDeclaration(node)) {
45606+
return grammarErrorOnNode(node.name, Diagnostics.Properties_with_the_accessor_modifier_are_only_available_when_targeting_ECMAScript_2015_and_higher);
45607+
}
45608+
if (isAutoAccessorPropertyDeclaration(node) && checkGrammarForInvalidQuestionMark(node.questionToken, Diagnostics.An_accessor_property_cannot_be_declared_optional)) {
45609+
return true;
45610+
}
4555645611
}
4555745612
else if (node.parent.kind === SyntaxKind.InterfaceDeclaration) {
4555845613
if (checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_an_interface_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type)) {

src/compiler/diagnosticMessages.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,14 @@
899899
"category": "Error",
900900
"code": 1274
901901
},
902+
"'accessor' modifier can only appear on a property declaration.": {
903+
"category": "Error",
904+
"code": 1275
905+
},
906+
"An 'accessor' property cannot be declared optional.": {
907+
"category": "Error",
908+
"code": 1276
909+
},
902910

903911
"'with' statements are not allowed in an async function block.": {
904912
"category": "Error",
@@ -7485,5 +7493,9 @@
74857493
"'{0}' is automatically exported here.": {
74867494
"category": "Message",
74877495
"code": 18044
7496+
},
7497+
"Properties with the 'accessor' modifier are only available when targeting ECMAScript 2015 and higher.": {
7498+
"category": "Error",
7499+
"code": 18045
74887500
}
74897501
}

0 commit comments

Comments
 (0)