Skip to content
Merged
24 changes: 24 additions & 0 deletions packages/eslint-plugin/src/rules/no-deprecated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,11 +274,35 @@ export default createRule({
);
}

function getJSXAttributeDeprecation(
openingElement: TSESTree.JSXOpeningElement,
propertyName: string,
): string | undefined {
const tsNode = services.esTreeNodeToTSNodeMap.get(openingElement.name);

const contextualType = nullThrows(
checker.getContextualType(tsNode as ts.Expression),
'Expected JSX opening element name to have contextualType',
);

const symbol = contextualType.getProperty(propertyName);

return getJsDocDeprecation(symbol);
}

function getDeprecationReason(node: IdentifierLike): string | undefined {
const callLikeNode = getCallLikeNode(node);
if (callLikeNode) {
return getCallLikeDeprecation(callLikeNode);
}

if (
node.parent.type === AST_NODE_TYPES.JSXAttribute &&
node.type !== AST_NODE_TYPES.Super
) {
return getJSXAttributeDeprecation(node.parent.parent, node.name);
}

if (
node.parent.type === AST_NODE_TYPES.Property &&
node.type !== AST_NODE_TYPES.Super
Expand Down
154 changes: 139 additions & 15 deletions packages/eslint-plugin/tests/rules/no-deprecated.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,21 +205,6 @@ ruleTester.run('no-deprecated', rule, {
default as ts,
} from 'typescript';
`,

// TODO: Can anybody figure out how to get this to report on `b`?
// getContextualType retrieves the union type, but it has no symbol...
`
interface AProps {
/** @deprecated */
b: number | string;
}

function A(props: AProps) {
return <div />;
}

const a = <A b="" />;
`,
`
namespace A {
/** @deprecated */
Expand Down Expand Up @@ -284,8 +269,75 @@ ruleTester.run('no-deprecated', rule, {
}
}
`,
`
declare namespace JSX {}

<foo bar={1} />;
`,
`
declare namespace JSX {
interface IntrinsicElements {
foo: any;
}
}

<foo bar={1} />;
`,
`
declare namespace JSX {
interface IntrinsicElements {
foo: unknown;
}
}

<foo bar={1} />;
`,
`
declare namespace JSX {
interface IntrinsicElements {
foo: {
bar: any;
};
}
}
<foo bar={1} />;
`,
`
declare namespace JSX {
interface IntrinsicElements {
foo: {
bar: unknown;
};
}
}
<foo bar={1} />;
`,
],
invalid: [
{
code: `
interface AProps {
/** @deprecated */
b: number | string;
}

function A(props: AProps) {
return <div />;
}

const a = <A b="" />;
`,
errors: [
{
column: 22,
data: { name: 'b' },
endColumn: 23,
endLine: 11,
line: 11,
messageId: 'deprecated',
},
],
},
{
code: `
/** @deprecated */ var a = undefined;
Expand Down Expand Up @@ -2550,5 +2602,77 @@ class B extends A {
},
],
},
{
code: 'const a = <div aria-grabbed></div>;',
errors: [
{
column: 16,
data: { name: 'aria-grabbed', reason: 'in ARIA 1.1' },
endColumn: 28,
endLine: 1,
line: 1,
messageId: 'deprecatedWithReason',
},
],
},
{
code: `
declare namespace JSX {
interface IntrinsicElements {
'foo-bar:baz-bam': {
name: string;
/**
* @deprecated
*/
deprecatedProp: string;
};
}
}

const componentDashed = <foo-bar:baz-bam name="e" deprecatedProp="oh no" />;
`,
errors: [
{
column: 59,
data: { name: 'deprecatedProp' },
endColumn: 73,
endLine: 14,
line: 14,
messageId: 'deprecated',
},
],
},
{
code: `
import * as React from 'react';

interface Props {
/**
* @deprecated
*/
deprecatedProp: string;
}

interface Tab {
List: React.FC<Props>;
}

const Tab: Tab = {
List: () => <div>Hi</div>,
};

const anotherExample = <Tab.List deprecatedProp="oh no" />;
`,
errors: [
{
column: 42,
data: { name: 'deprecatedProp' },
endColumn: 56,
endLine: 19,
line: 19,
messageId: 'deprecated',
},
],
},
],
});
Loading