Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork2.8k
feat(eslint-plugin): [consistent-type-assertions] add arrayLiteralTypeAssertions options#10565
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
e4b6227
9dfe2e8
880789a
8fcf981
7d377d1
ee8a2ef
0af07a9
b64df60
c3f66e5
fdcd4ce
f78ba99
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -18,19 +18,27 @@ export type MessageIds = | ||
| 'angle-bracket' | ||
| 'as' | ||
| 'never' | ||
| 'replaceArrayTypeAssertionWithAnnotation' | ||
| 'replaceArrayTypeAssertionWithSatisfies' | ||
| 'replaceObjectTypeAssertionWithAnnotation' | ||
| 'replaceObjectTypeAssertionWithSatisfies' | ||
| 'unexpectedArrayTypeAssertion' | ||
| 'unexpectedObjectTypeAssertion'; | ||
type OptUnion = | ||
| { | ||
assertionStyle: 'angle-bracket' | 'as'; | ||
objectLiteralTypeAssertions?: 'allow' | 'allow-as-parameter' | 'never'; | ||
arrayLiteralTypeAssertions?: 'allow' | 'allow-as-parameter' | 'never'; | ||
} | ||
| { | ||
assertionStyle: 'never'; | ||
}; | ||
export type Options = readonly [OptUnion]; | ||
type AsExpressionOrTypeAssertion = | ||
| TSESTree.TSAsExpression | ||
| TSESTree.TSTypeAssertion; | ||
export default createRule<Options, MessageIds>({ | ||
name: 'consistent-type-assertions', | ||
meta: { | ||
@@ -45,10 +53,15 @@ export default createRule<Options, MessageIds>({ | ||
'angle-bracket': "Use '<{{cast}}>' instead of 'as {{cast}}'.", | ||
as: "Use 'as {{cast}}' instead of '<{{cast}}>'.", | ||
never: 'Do not use any type assertions.', | ||
replaceArrayTypeAssertionWithAnnotation: | ||
'Use const x: {{cast}} = [ ... ] instead.', | ||
replaceArrayTypeAssertionWithSatisfies: | ||
'Use const x = [ ... ] satisfies {{cast}} instead.', | ||
replaceObjectTypeAssertionWithAnnotation: | ||
'Use const x: {{cast}} = { ... } instead.', | ||
replaceObjectTypeAssertionWithSatisfies: | ||
'Use const x = { ... } satisfies {{cast}} instead.', | ||
unexpectedArrayTypeAssertion: 'Always prefer const x: T[] = [ ... ].', | ||
unexpectedObjectTypeAssertion: 'Always prefer const x: T = { ... }.', | ||
}, | ||
schema: [ | ||
@@ -70,6 +83,12 @@ export default createRule<Options, MessageIds>({ | ||
type: 'object', | ||
additionalProperties: false, | ||
properties: { | ||
arrayLiteralTypeAssertions: { | ||
type: 'string', | ||
description: | ||
'Whether to always prefer type declarations for array literals used as variable initializers, rather than type assertions.', | ||
enum: ['allow', 'allow-as-parameter', 'never'], | ||
}, | ||
assertionStyle: { | ||
type: 'string', | ||
description: 'The expected assertion style to enforce.', | ||
@@ -89,6 +108,7 @@ export default createRule<Options, MessageIds>({ | ||
}, | ||
defaultOptions: [ | ||
{ | ||
arrayLiteralTypeAssertions: 'allow', | ||
assertionStyle: 'as', | ||
objectLiteralTypeAssertions: 'allow', | ||
}, | ||
@@ -106,7 +126,7 @@ export default createRule<Options, MessageIds>({ | ||
} | ||
function reportIncorrectAssertionType( | ||
node:AsExpressionOrTypeAssertion, | ||
): void { | ||
const messageId = options.assertionStyle; | ||
@@ -192,8 +212,63 @@ export default createRule<Options, MessageIds>({ | ||
} | ||
} | ||
function getSuggestions( | ||
node: AsExpressionOrTypeAssertion, | ||
annotationMessageId: MessageIds, | ||
satisfiesMessageId: MessageIds, | ||
): TSESLint.ReportSuggestionArray<MessageIds> { | ||
const suggestions: TSESLint.ReportSuggestionArray<MessageIds> = []; | ||
if ( | ||
node.parent.type === AST_NODE_TYPES.VariableDeclarator && | ||
!node.parent.id.typeAnnotation | ||
) { | ||
const { parent } = node; | ||
suggestions.push({ | ||
messageId: annotationMessageId, | ||
data: { cast: context.sourceCode.getText(node.typeAnnotation) }, | ||
fix: fixer => [ | ||
fixer.insertTextAfter( | ||
parent.id, | ||
`: ${context.sourceCode.getText(node.typeAnnotation)}`, | ||
), | ||
fixer.replaceText( | ||
node, | ||
getTextWithParentheses(context.sourceCode, node.expression), | ||
), | ||
], | ||
}); | ||
} | ||
suggestions.push({ | ||
messageId: satisfiesMessageId, | ||
data: { cast: context.sourceCode.getText(node.typeAnnotation) }, | ||
fix: fixer => [ | ||
fixer.replaceText( | ||
node, | ||
getTextWithParentheses(context.sourceCode, node.expression), | ||
), | ||
fixer.insertTextAfter( | ||
node, | ||
` satisfies ${context.sourceCode.getText(node.typeAnnotation)}`, | ||
), | ||
], | ||
}); | ||
return suggestions; | ||
} | ||
function isAsParameter(node: AsExpressionOrTypeAssertion): boolean { | ||
return ( | ||
node.parent.type === AST_NODE_TYPES.NewExpression || | ||
node.parent.type === AST_NODE_TYPES.CallExpression || | ||
node.parent.type === AST_NODE_TYPES.ThrowStatement || | ||
node.parent.type === AST_NODE_TYPES.AssignmentPattern || | ||
node.parent.type === AST_NODE_TYPES.JSXExpressionContainer || | ||
(node.parent.type === AST_NODE_TYPES.TemplateLiteral && | ||
node.parent.parent.type === AST_NODE_TYPES.TaggedTemplateExpression) | ||
Comment on lines +260 to +266 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. [Testing]: Some of these can be removed with no tests failing, I think it would be helpful to have these covered by tests. | ||
); | ||
} | ||
function checkExpressionForObjectAssertion( | ||
node: AsExpressionOrTypeAssertion, | ||
): void { | ||
if ( | ||
options.assertionStyle === 'never' || | ||
@@ -205,54 +280,17 @@ export default createRule<Options, MessageIds>({ | ||
if ( | ||
options.objectLiteralTypeAssertions === 'allow-as-parameter' && | ||
isAsParameter(node) | ||
) { | ||
return; | ||
} | ||
if (checkType(node.typeAnnotation)) { | ||
const suggest = getSuggestions( | ||
node, | ||
'replaceObjectTypeAssertionWithAnnotation', | ||
'replaceObjectTypeAssertionWithSatisfies', | ||
); | ||
context.report({ | ||
node, | ||
@@ -262,22 +300,57 @@ export default createRule<Options, MessageIds>({ | ||
} | ||
} | ||
function checkExpressionForArrayAssertion( | ||
node: AsExpressionOrTypeAssertion, | ||
): void { | ||
if ( | ||
options.assertionStyle === 'never' || | ||
options.arrayLiteralTypeAssertions === 'allow' || | ||
node.expression.type !== AST_NODE_TYPES.ArrayExpression | ||
) { | ||
return; | ||
} | ||
if ( | ||
options.arrayLiteralTypeAssertions === 'allow-as-parameter' && | ||
isAsParameter(node) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. [Testing]: This can be removed and no tests fail ( | ||
) { | ||
return; | ||
} | ||
if (checkType(node.typeAnnotation)) { | ||
const suggest = getSuggestions( | ||
node, | ||
'replaceArrayTypeAssertionWithAnnotation', | ||
'replaceArrayTypeAssertionWithSatisfies', | ||
); | ||
context.report({ | ||
node, | ||
messageId: 'unexpectedArrayTypeAssertion', | ||
suggest, | ||
}); | ||
} | ||
} | ||
return { | ||
TSAsExpression(node): void { | ||
if (options.assertionStyle !== 'as') { | ||
reportIncorrectAssertionType(node); | ||
return; | ||
} | ||
checkExpressionForObjectAssertion(node); | ||
checkExpressionForArrayAssertion(node); | ||
}, | ||
TSTypeAssertion(node): void { | ||
if (options.assertionStyle !== 'angle-bracket') { | ||
reportIncorrectAssertionType(node); | ||
return; | ||
} | ||
checkExpressionForObjectAssertion(node); | ||
checkExpressionForArrayAssertion(node); | ||
}, | ||
}; | ||
}, | ||
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.