diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d8264d9f849..dcc7adeac6f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17019,7 +17019,7 @@ namespace ts { containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined ): boolean { - if (isTypeRelatedTo(source, target, relation, /*reportDeprecatedProperties*/ true)) return true; + if (isTypeRelatedTo(source, target, relation)) return true; if (!errorNode || !elaborateError(expr, source, target, relation, headMessage, containingMessageChain, errorOutputContainer)) { return checkTypeRelatedTo(source, target, relation, errorNode, headMessage, containingMessageChain, errorOutputContainer); } @@ -17788,7 +17788,7 @@ namespace ts { return false; } - function isTypeRelatedTo(source: Type, target: Type, relation: ESMap, reportDeprecatedProperties?: boolean) { + function isTypeRelatedTo(source: Type, target: Type, relation: ESMap) { if (isFreshLiteralType(source)) { source = (source as FreshableType).regularType; } @@ -17815,8 +17815,7 @@ namespace ts { } if (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable) { return checkTypeRelatedTo(source, target, relation, - /*errorNode*/ undefined, /*headMessage*/ undefined, /*containingMessageChain*/ undefined, /*errorOutputContainer*/ undefined, - reportDeprecatedProperties); + /*errorNode*/ undefined, /*headMessage*/ undefined, /*containingMessageChain*/ undefined, /*errorOutputContainer*/ undefined); } return false; } @@ -17850,7 +17849,6 @@ namespace ts { * @param headMessage If the error chain should be prepended by a head message, then headMessage will be used. * @param containingMessageChain A chain of errors to prepend any new errors found. * @param errorOutputContainer Return the diagnostic. Do not log if 'skipLogging' is truthy. - * @param reportDeprecatedProperties Properties with deprecated annotation will be reported, if result is true. */ function checkTypeRelatedTo( source: Type, @@ -17860,7 +17858,6 @@ namespace ts { headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined, errorOutputContainer?: { errors?: Diagnostic[], skipLogging?: boolean }, - reportDeprecatedProperties?: boolean ): boolean { let errorInfo: DiagnosticMessageChain | undefined; @@ -17877,7 +17874,6 @@ namespace ts { let lastSkippedInfo: [Type, Type] | undefined; let incompatibleStack: [DiagnosticMessage, (string | number)?, (string | number)?, (string | number)?, (string | number)?][] = []; let inPropertyCheck = false; - const deprecatedSuggestions: Parameters[] = []; Debug.assert(relation !== identityRelation || !errorNode, "no error reporting in identity checking"); @@ -17929,15 +17925,7 @@ namespace ts { Debug.assert(!!errorOutputContainer.errors, "missed opportunity to interact with error."); } - const isRelated = result !== Ternary.False; - - if (isRelated) { - for (const args of deprecatedSuggestions) { - addDeprecatedSuggestion(...args); - } - } - - return isRelated; + return result !== Ternary.False; function resetErrorInfo(saved: ReturnType) { errorInfo = saved.errorInfo; @@ -18271,9 +18259,6 @@ namespace ts { } return Ternary.False; } - if (reportDeprecatedProperties) { - checkDeprecatedProperties(source as FreshObjectLiteralType, target); - } } const isPerformingCommonPropertyChecks = relation !== comparableRelation && !(intersectionState & IntersectionState.Target) && @@ -18550,27 +18535,6 @@ namespace ts { return prop.valueDeclaration && container.valueDeclaration && prop.valueDeclaration.parent === container.valueDeclaration; } - function checkDeprecatedProperties(source: FreshObjectLiteralType, target: Type) { - if (!isExcessPropertyCheckTarget(target)) { - return; - } - - for (const prop of getPropertiesOfType(source)) { - if (!shouldCheckAsExcessProperty(prop, source.symbol)) { - continue; - } - - const symbol = getPropertyOfObjectType(target, prop.escapedName); - if (symbol?.declarations?.some(decl => decl.flags & NodeFlags.Deprecated)) { - const node = prop.valueDeclaration!; - const nameNode = isPropertyAssignment(node) || isShorthandPropertyAssignment(node) || isObjectLiteralMethod(node) || isJsxAttribute(node) - ? node.name - : prop.valueDeclaration!; - deprecatedSuggestions.push([nameNode, symbol.declarations, prop.escapedName as string]); - } - } - } - function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType): Ternary { let result = Ternary.True; const sourceTypes = source.types; @@ -27170,6 +27134,17 @@ namespace ts { symbolToString(member), typeToString(contextualType)); } } + if (contextualType) { + const contextualProperty = getPropertyOfType(contextualType, member.escapedName) + const isFromOverload = contextualProperty?.declarations?.some(decl => { + const param = findAncestor(decl, isParameter) + return param && isFunctionDeclaration(param.parent) && countWhere(getSymbolOfNode(param.parent).declarations, isFunctionLike) > 1 + }) + if (!isFromOverload) { + checkDeprecatedProperty(member, contextualProperty) + } + + } prop.declarations = member.declarations; prop.parent = member.parent; @@ -27881,10 +27856,17 @@ namespace ts { } if (isNodeOpeningLikeElement) { - const jsxOpeningLikeNode = node as JsxOpeningLikeElement; - const sig = getResolvedSignature(jsxOpeningLikeNode); - checkDeprecatedSignature(sig, node as JsxOpeningLikeElement); - checkJsxReturnAssignableToAppropriateBound(getJsxReferenceKind(jsxOpeningLikeNode), getReturnTypeOfSignature(sig), jsxOpeningLikeNode); + const sig = getResolvedSignature(node); + checkDeprecatedSignature(sig, node); + const param = sig.parameters[0] + for (const source of node.attributes.properties) { + const member = source.symbol; + const attributesTarget = getTypeOfSymbol(param) + if (member && attributesTarget) { + checkDeprecatedProperty(member, getPropertyOfType(attributesTarget, member.escapedName)) + } + } + checkJsxReturnAssignableToAppropriateBound(getJsxReferenceKind(node), getReturnTypeOfSignature(sig), node); } } @@ -31108,6 +31090,16 @@ namespace ts { } } + function checkDeprecatedProperty(source: Symbol, target: Symbol | undefined) { + if (target?.declarations?.some(decl => decl.flags & NodeFlags.Deprecated)) { + const node = source.valueDeclaration!; + const nameNode = isPropertyAssignment(node) || isShorthandPropertyAssignment(node) || isObjectLiteralMethod(node) || isJsxAttribute(node) + ? node.name + : source.valueDeclaration!; + addDeprecatedSuggestion(nameNode, target.declarations, source.escapedName as string); + } + } + function getDeprecatedSuggestionNode(node: Node): Node { node = skipParentheses(node); switch (node.kind) { diff --git a/tests/cases/fourslash/jsdocDeprecated_suggestion14_objectLiteralAssignments.ts b/tests/cases/fourslash/jsdocDeprecated_suggestion14_objectLiteralAssignments.ts index 4a396fced98..54606b7d49a 100644 --- a/tests/cases/fourslash/jsdocDeprecated_suggestion14_objectLiteralAssignments.ts +++ b/tests/cases/fourslash/jsdocDeprecated_suggestion14_objectLiteralAssignments.ts @@ -22,7 +22,7 @@ //// props = { bar, [|callback|]() {} }; //// // Skip if there is a type incompatibility error. -//// const props5: Props = { foo: "foo", boo: "boo" }; +//// const props5: Props = { [|foo|]: "foo", boo: "boo" }; //// // Skip for union types. //// const props6: { foo: { /** @deprecated */ bar: string } | { bar: string, baz: string } } = { foo: { bar: "bar" } }; @@ -36,22 +36,52 @@ verify.getSuggestionDiagnostics([ range: ranges[0], reportsDeprecated: true, }, - ...ranges.slice(1, ranges.length - 2).map(range => ({ + { message: "'foo' is deprecated.", code: 6385, - range, + range: ranges[1], reportsDeprecated: true as const, - })), + }, + { + message: "'foo' is deprecated.", + code: 6385, + range: ranges[2], + reportsDeprecated: true as const, + }, + { + message: "'foo' is deprecated.", + code: 6385, + range: ranges[3], + reportsDeprecated: true as const, + }, + { + message: "'foo' is deprecated.", + code: 6385, + range: ranges[4], + reportsDeprecated: true as const, + }, + { + message: "'foo' is deprecated.", + code: 6385, + range: ranges[5], + reportsDeprecated: true as const, + }, { message: "'callback' is deprecated.", code: 6385, - range: ranges[ranges.length - 2], + range: ranges[6], reportsDeprecated: true, }, { message: "'callback' is deprecated.", code: 6385, - range: ranges[ranges.length - 1], + range: ranges[7], reportsDeprecated: true, }, + { + message: "'foo' is deprecated.", + code: 6385, + range: ranges[8], + reportsDeprecated: true as const, + }, ]) diff --git a/tests/cases/fourslash/jsdocDeprecated_suggestion15_objectLiteralArguments.ts b/tests/cases/fourslash/jsdocDeprecated_suggestion15_objectLiteralArguments.ts index ae5925c2bd9..05cd88932e6 100644 --- a/tests/cases/fourslash/jsdocDeprecated_suggestion15_objectLiteralArguments.ts +++ b/tests/cases/fourslash/jsdocDeprecated_suggestion15_objectLiteralArguments.ts @@ -21,8 +21,8 @@ //// func({ [|foo|], bar, [|callback|] }); //// func({ bar, [|callback|]() {} }); -//// // Skip if there is a type incompatibility error. -//// func({ foo: "foo", boo: "boo" }); +//// // Do not skip if there is a type incompatibility error. +//// func({ [|foo|]: "foo", boo: "boo" }); //// // Skip for union types. //// function test(_args: { foo: { /** @deprecated */ bar: string } | { bar: string, baz: string } }) {} @@ -37,22 +37,52 @@ verify.getSuggestionDiagnostics([ range: ranges[0], reportsDeprecated: true, }, - ...ranges.slice(1, ranges.length - 2).map(range => ({ + { message: "'foo' is deprecated.", code: 6385, - range, + range: ranges[1], reportsDeprecated: true as const, - })), + }, + { + message: "'foo' is deprecated.", + code: 6385, + range: ranges[2], + reportsDeprecated: true as const, + }, + { + message: "'foo' is deprecated.", + code: 6385, + range: ranges[3], + reportsDeprecated: true as const, + }, + { + message: "'foo' is deprecated.", + code: 6385, + range: ranges[4], + reportsDeprecated: true as const, + }, + { + message: "'foo' is deprecated.", + code: 6385, + range: ranges[5], + reportsDeprecated: true as const, + }, { message: "'callback' is deprecated.", code: 6385, - range: ranges[ranges.length - 2], + range: ranges[6], reportsDeprecated: true, }, { message: "'callback' is deprecated.", code: 6385, - range: ranges[ranges.length - 1], + range: ranges[7], + reportsDeprecated: true, + }, + { + message: "'foo' is deprecated.", + code: 6385, + range: ranges[8], reportsDeprecated: true, }, ]) diff --git a/tests/cases/fourslash/jsdocDeprecated_suggestion16_jsxArguments.ts b/tests/cases/fourslash/jsdocDeprecated_suggestion16_jsxArguments.ts index 096137ef917..0801cc0db52 100644 --- a/tests/cases/fourslash/jsdocDeprecated_suggestion16_jsxArguments.ts +++ b/tests/cases/fourslash/jsdocDeprecated_suggestion16_jsxArguments.ts @@ -1,3 +1,4 @@ +/// // @Filename: a.tsx //// type Props = { //// /** @deprecated */ @@ -19,16 +20,16 @@ //// {}} [|foo|]="foo" bar="bar" baz={{ [|foo|]: true }} />; -//// // Skip spread in jsx. -//// ; +//// // Do not skip spread in jsx +//// ; -//// // Skip if there is a type incompatibility error. -//// ; -//// ; +//// // Do not skip if there is a type incompatibility error. +//// ; +//// ; //// // Skip for union types. //// const Component2 = (_props: { foo: { /** @deprecated */ bar: string } | { bar: string, baz: string } }) =>
; -//// ; +//// ; goTo.file('a.tsx') const ranges = test.ranges(); @@ -46,10 +47,34 @@ verify.getSuggestionDiagnostics([ range: ranges[1], reportsDeprecated: true, }, - ...ranges.slice(2).map(range => ({ + { message: "'foo' is deprecated.", code: 6385, - range, + range: ranges[2], reportsDeprecated: true as const, - })) + }, + { + message: "'foo' is deprecated.", + code: 6385, + range: ranges[3], + reportsDeprecated: true as const, + }, + { + message: "'foo' is deprecated.", + code: 6385, + range: ranges[4], + reportsDeprecated: true as const, + }, + { + message: "'foo' is deprecated.", + code: 6385, + range: ranges[5], + reportsDeprecated: true as const, + }, + { + message: "'foo' is deprecated.", + code: 6385, + range: ranges[6], + reportsDeprecated: true as const, + }, ]) diff --git a/tests/cases/fourslash/jsdocDeprecated_suggestion18_functionArgumentsWithOverloads.ts b/tests/cases/fourslash/jsdocDeprecated_suggestion18_functionArgumentsWithOverloads.ts index 76bc8416eea..028ebaf38f1 100644 --- a/tests/cases/fourslash/jsdocDeprecated_suggestion18_functionArgumentsWithOverloads.ts +++ b/tests/cases/fourslash/jsdocDeprecated_suggestion18_functionArgumentsWithOverloads.ts @@ -8,16 +8,9 @@ //// return
; //// } -//// overloadFunc({ [|a|]: true }) />; -//// overloadFunc({ a: true, b: true }) />; +//// overloadFunc({ [|a|]: true }); +//// overloadFunc({ a: true, b: true }); const ranges = test.ranges(); -verify.getSuggestionDiagnostics([ - { - message: "'a' is deprecated.", - code: 6385, - range: ranges[0], - reportsDeprecated: true, - } -]) +verify.getSuggestionDiagnostics([])