diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 8fa3c4fce2a4a..b0711c3467296 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -2605,14 +2605,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return diagnostic;
}
- function isDeprecatedSymbol(symbol: Symbol) {
- const parentSymbol = getParentOfSymbol(symbol);
- if (parentSymbol && length(symbol.declarations) > 1) {
- return parentSymbol.flags & SymbolFlags.Interface ? some(symbol.declarations, isDeprecatedDeclaration) : every(symbol.declarations, isDeprecatedDeclaration);
- }
- return !!symbol.valueDeclaration && isDeprecatedDeclaration(symbol.valueDeclaration)
- || length(symbol.declarations) && every(symbol.declarations, isDeprecatedDeclaration);
- }
+ function isDeprecatedSymbol(symbol: Symbol) {
+ const parentSymbol = getParentOfSymbol(symbol);
+ if (parentSymbol && length(symbol.declarations) > 1) {
+ if (parentSymbol.flags & SymbolFlags.Interface || symbol.flags & SymbolFlags.Accessor) {
+ return some(symbol.declarations, isDeprecatedDeclaration);
+ }
+ return every(symbol.declarations, isDeprecatedDeclaration);
+ }
+ return !!symbol.valueDeclaration && isDeprecatedDeclaration(symbol.valueDeclaration)
+ || length(symbol.declarations) && every(symbol.declarations, isDeprecatedDeclaration);
+ }
function isDeprecatedDeclaration(declaration: Declaration) {
return !!(getCombinedNodeFlagsCached(declaration) & NodeFlags.Deprecated);
diff --git a/src/services/completions.ts b/src/services/completions.ts
index 28d29136dab89..368829c4e6a8a 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -6084,10 +6084,14 @@ function symbolCanBeReferencedAtTypeLocation(symbol: Symbol, checker: TypeChecke
}
}
-function isDeprecated(symbol: Symbol, checker: TypeChecker) {
- const declarations = skipAlias(symbol, checker).declarations;
- return !!length(declarations) && every(declarations, isDeprecatedDeclaration);
-}
+function isDeprecated(symbol: Symbol, checker: TypeChecker) {
+ const resolvedSymbol = skipAlias(symbol, checker);
+ const declarations = resolvedSymbol.declarations;
+ if (resolvedSymbol.flags & SymbolFlags.Accessor) {
+ return !!length(declarations) && some(declarations, isDeprecatedDeclaration);
+ }
+ return !!length(declarations) && every(declarations, isDeprecatedDeclaration);
+}
/**
* True if the first character of `lowercaseCharacters` is the first character
diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts
index 03b2b59193621..c7778ffc2becf 100644
--- a/src/services/symbolDisplay.ts
+++ b/src/services/symbolDisplay.ts
@@ -213,20 +213,25 @@ function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeCheck
return ScriptElementKind.unknown;
}
-function getNormalizedSymbolModifiers(symbol: Symbol) {
- if (symbol.declarations && symbol.declarations.length) {
- const [declaration, ...declarations] = symbol.declarations;
- // omit deprecated flag if some declarations are not deprecated
- const excludeFlags = length(declarations) && isDeprecatedDeclaration(declaration) && some(declarations, d => !isDeprecatedDeclaration(d))
- ? ModifierFlags.Deprecated
- : ModifierFlags.None;
- const modifiers = getNodeModifiers(declaration, excludeFlags);
- if (modifiers) {
- return modifiers.split(",");
- }
- }
- return [];
-}
+function getNormalizedSymbolModifiers(symbol: Symbol) {
+ if (symbol.declarations && symbol.declarations.length) {
+ const [declaration, ...declarations] = symbol.declarations;
+ // omit deprecated flag if some declarations are not deprecated
+ const excludeFlags = length(declarations) && isDeprecatedDeclaration(declaration) && some(declarations, d => !isDeprecatedDeclaration(d))
+ ? ModifierFlags.Deprecated
+ : ModifierFlags.None;
+ const modifiers = getNodeModifiers(declaration, excludeFlags);
+ if (modifiers) {
+ const result = modifiers.split(",");
+ // For getter/setter pairs, include deprecated if any accessor has @deprecated
+ if (symbol.flags & SymbolFlags.Accessor && !result.includes("deprecated") && some(symbol.declarations, isDeprecatedDeclaration)) {
+ result.push("deprecated");
+ }
+ return result;
+ }
+ }
+ return [];
+}
/** @internal */
export function getSymbolModifiers(typeChecker: TypeChecker, symbol: Symbol): string {
diff --git a/tests/cases/fourslash/completionsWithDeprecatedGetterSetter.ts b/tests/cases/fourslash/completionsWithDeprecatedGetterSetter.ts
new file mode 100644
index 0000000000000..54053a447cc43
--- /dev/null
+++ b/tests/cases/fourslash/completionsWithDeprecatedGetterSetter.ts
@@ -0,0 +1,35 @@
+///
+
+// Tests that @deprecated on getter/setter pairs correctly marks the property as deprecated in completions.
+// Regression from #41941, reported in #62965.
+
+//// class Test {
+//// /** @deprecated */
+//// public get test1() { return 0; }
+//// public set test1(_value: number) {}
+////
+//// public get test2() { return 0; }
+//// /** @deprecated */
+//// public set test2(_value: number) {}
+////
+//// /** @deprecated */
+//// public get test3() { return 0; }
+//// /** @deprecated */
+//// public set test3(_value: number) {}
+////
+//// public get test4() { return 0; }
+//// public set test4(_value: number) {}
+//// }
+////
+//// const t = new Test();
+//// t./*1*/;
+
+verify.completions({
+ marker: "1",
+ includes: [
+ { name: "test1", kind: "getter", kindModifiers: "public,deprecated", sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) },
+ { name: "test2", kind: "getter", kindModifiers: "public,deprecated", sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) },
+ { name: "test3", kind: "getter", kindModifiers: "public,deprecated", sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) },
+ { name: "test4", kind: "getter", kindModifiers: "public", sortText: completion.SortText.LocationPriority },
+ ],
+});
diff --git a/tests/cases/fourslash/jsdocDeprecated_suggestionGetterSetter.ts b/tests/cases/fourslash/jsdocDeprecated_suggestionGetterSetter.ts
new file mode 100644
index 0000000000000..4e40b678f9f34
--- /dev/null
+++ b/tests/cases/fourslash/jsdocDeprecated_suggestionGetterSetter.ts
@@ -0,0 +1,50 @@
+///
+
+// Tests that @deprecated on getter/setter pairs produces suggestion diagnostics.
+// Regression from #41941, reported in #62965.
+
+//// class Test {
+//// /** @deprecated */
+//// public get test1() { return 0; }
+//// public set test1(_value: number) {}
+////
+//// public get test2() { return 0; }
+//// /** @deprecated */
+//// public set test2(_value: number) {}
+////
+//// /** @deprecated */
+//// public get test3() { return 0; }
+//// /** @deprecated */
+//// public set test3(_value: number) {}
+////
+//// public get test4() { return 0; }
+//// public set test4(_value: number) {}
+//// }
+////
+//// const t = new Test();
+//// const a = t.[|test1|];
+//// const b = t.[|test2|];
+//// const c = t.[|test3|];
+//// const d = t.test4;
+
+const ranges = test.ranges();
+verify.getSuggestionDiagnostics([
+ {
+ "code": 6385,
+ "message": "'test1' is deprecated.",
+ "reportsDeprecated": true,
+ "range": ranges[0],
+ },
+ {
+ "code": 6385,
+ "message": "'test2' is deprecated.",
+ "reportsDeprecated": true,
+ "range": ranges[1],
+ },
+ {
+ "code": 6385,
+ "message": "'test3' is deprecated.",
+ "reportsDeprecated": true,
+ "range": ranges[2],
+ },
+]);