diff --git a/src/rules/jsxKeyRule.ts b/src/rules/jsxKeyRule.ts index 1f9dd4d..ee12741 100644 --- a/src/rules/jsxKeyRule.ts +++ b/src/rules/jsxKeyRule.ts @@ -22,9 +22,12 @@ import { isBlock, isCallExpression, isFunctionExpression, + isIdentifier, isJsxAttribute, isJsxElement, isJsxSelfClosingElement, + isJsxSpreadAttribute, + isObjectLiteralExpression, isParenthesizedExpression, isPropertyAccessExpression, isReturnStatement, @@ -88,11 +91,12 @@ function walk(ctx: Lint.WalkContext): void { } function checkIteratorElement(node: ts.Node, ctx: Lint.WalkContext) { - if (isJsxElement(node) && !hasKeyProp(node.openingElement.attributes)) { + if (isJsxElement(node) && !hasKeyProp(node.openingElement.attributes) && + !hasKeyPropSpread(node.openingElement.attributes)) { ctx.addFailureAtNode(node, Rule.FAILURE_STRING); } - if (isJsxSelfClosingElement(node) && !hasKeyProp(node.attributes)) { + if (isJsxSelfClosingElement(node) && !hasKeyProp(node.attributes) && !hasKeyPropSpread(node.attributes)) { ctx.addFailureAtNode(node, Rule.FAILURE_STRING); } } @@ -103,6 +107,16 @@ function hasKeyProp(attributes: ts.JsxAttributes) { .indexOf(true) !== -1; } +function hasKeyPropSpread(attributes: ts.JsxAttributes) { + return attributes.properties.some((prop) => ( + isJsxSpreadAttribute(prop) && + isObjectLiteralExpression(prop.expression) && + prop.expression.properties.some((expProp) => ( + expProp.name !== undefined && isIdentifier(expProp.name) && expProp.name.text === "key" + )) + )); +} + function getReturnStatement(body: ts.NodeArray) { return body.filter((item) => isReturnStatement(item))[0] as ts.ReturnStatement | undefined; } diff --git a/test/rules/jsx-key/test.tsx.lint b/test/rules/jsx-key/test.tsx.lint index 73a4dd2..5cc225e 100644 --- a/test/rules/jsx-key/test.tsx.lint +++ b/test/rules/jsx-key/test.tsx.lint @@ -10,6 +10,8 @@ var App = () =>
; [1, 2, 3].map(function(x) { return; }); foo(() =>
) []; +[1, 2, 3].map((x, idx) => ); +[1, 2, 3].map((x, key) => ); []; ~~~~~~~~~~~~~~~~ [0]