📄 consistent-type-assertions¶
📊 Analysis Summary¶
| Metric | Count |
|---|---|
| 🔧 Functions | 8 |
| 📦 Imports | 10 |
| 📑 Type Aliases | 4 |
📚 Table of Contents¶
🛠️ File Location:¶
📂 packages/eslint-plugin/src/rules/consistent-type-assertions.ts
📤 Default Export¶
| Property | Value |
|---|---|
name |
'consistent-type-assertions' |
meta.type |
'suggestion' |
meta.docs.description |
'Enforce consistent usage of type assertions' |
meta.docs.recommended |
'stylistic' |
meta.fixable |
'code' |
meta.hasSuggestions |
true |
meta.messages.as |
"Use 'as {{cast}}' instead of '<{{cast}}>'." |
meta.messages.never |
'Do not use any type assertions.' |
meta.messages.replaceArrayTypeAssertionWithAnnotation |
'Use const x: {{cast}} = [ ... ] instead.' |
meta.messages.replaceArrayTypeAssertionWithSatisfies |
'Use const x = [ ... ] satisfies {{cast}} instead.' |
meta.messages.replaceObjectTypeAssertionWithAnnotation |
'Use const x: {{cast}} = { ... } instead.' |
meta.messages.replaceObjectTypeAssertionWithSatisfies |
'Use const x = { ... } satisfies {{cast}} instead.' |
meta.messages.unexpectedArrayTypeAssertion |
'Always prefer const x: T[] = [ ... ].' |
meta.messages.unexpectedObjectTypeAssertion |
'Always prefer const x: T = { ... }.' |
meta.schema |
[ { oneOf: [ { type: 'object', additionalProperties: false, properties: { assertionStyle: { type: 'string', descripti... |
defaultOptions |
[ { arrayLiteralTypeAssertions: 'allow', assertionStyle: 'as', objectLiteralTypeAssertions: 'allow', }, ] |
Entry point: create — documented under Functions.
📦 Imports¶
| Name | Source |
|---|---|
TSESLint |
@typescript-eslint/utils |
TSESTree |
@typescript-eslint/utils |
AST_NODE_TYPES |
@typescript-eslint/utils |
createRule |
../util |
getOperatorPrecedence |
../util |
getOperatorPrecedenceForNode |
../util |
getParserServices |
../util |
getTextWithParentheses |
../util |
isParenthesized |
../util |
getWrappedCode |
../util/getWrappedCode |
Functions¶
create(context: any, [options]: any): { TSAsExpression(node: any): void; TSTypeAssertion(node: an…¶
Parameters:
contextany[options]any
Returns: { TSAsExpression(node: any): void; TSTypeAssertion(node: any): void; }
Calls:
isConstcontext.reportcontext.sourceCode.getTextgetParserServices( context, true, ).esTreeNodeToTSNodeMap.getgetOperatorPrecedence (from ../util)ts.isBinaryExpressionts.isNewExpressiongetOperatorPrecedenceForNode (from ../util)getWrappedCode (from ../util/getWrappedCode)fixer.replaceTextisParenthesized (from ../util)suggestions.pushfixer.insertTextAftergetTextWithParentheses (from ../util)isAsParametercheckTypegetSuggestionsreportIncorrectAssertionTypecheckExpressionForObjectAssertioncheckExpressionForArrayAssertion
Internal Comments:
// If this node is `as const`, then don't report an error.
// lazily access parserServices to avoid crashing on non TS files (#9860) (x2)
// Ignore `as const` and `<const>` (x2)
// Allow qualified names which have dots between identifiers, `Foo.Bar` (x4)
Code
create(context, [options]) {
function isConst(node: TSESTree.TypeNode): boolean {
if (node.type !== AST_NODE_TYPES.TSTypeReference) {
return false;
}
return (
node.typeName.type === AST_NODE_TYPES.Identifier &&
node.typeName.name === 'const'
);
}
function reportIncorrectAssertionType(
node: AsExpressionOrTypeAssertion,
): void {
const messageId = options.assertionStyle;
// If this node is `as const`, then don't report an error.
if (isConst(node.typeAnnotation) && messageId === 'never') {
return;
}
context.report({
node,
messageId,
data:
messageId !== 'never'
? { cast: context.sourceCode.getText(node.typeAnnotation) }
: {},
fix:
messageId === 'as'
? (fixer): TSESLint.RuleFix => {
// lazily access parserServices to avoid crashing on non TS files (#9860)
const tsNode = getParserServices(
context,
true,
).esTreeNodeToTSNodeMap.get(node as TSESTree.TSTypeAssertion);
const expressionCode = context.sourceCode.getText(
node.expression,
);
const typeAnnotationCode = context.sourceCode.getText(
node.typeAnnotation,
);
const asPrecedence = getOperatorPrecedence(
ts.SyntaxKind.AsExpression,
ts.SyntaxKind.Unknown,
);
const parentPrecedence = getOperatorPrecedence(
tsNode.parent.kind,
ts.isBinaryExpression(tsNode.parent)
? tsNode.parent.operatorToken.kind
: ts.SyntaxKind.Unknown,
ts.isNewExpression(tsNode.parent)
? tsNode.parent.arguments != null &&
tsNode.parent.arguments.length > 0
: undefined,
);
const expressionPrecedence = getOperatorPrecedenceForNode(
node.expression,
);
const expressionCodeWrapped = getWrappedCode(
expressionCode,
expressionPrecedence,
asPrecedence,
);
const text = `${expressionCodeWrapped} as ${typeAnnotationCode}`;
return fixer.replaceText(
node,
isParenthesized(node, context.sourceCode)
? text
: getWrappedCode(text, asPrecedence, parentPrecedence),
);
}
: undefined,
});
}
function checkType(node: TSESTree.TypeNode): boolean {
switch (node.type) {
case AST_NODE_TYPES.TSAnyKeyword:
case AST_NODE_TYPES.TSUnknownKeyword:
return false;
case AST_NODE_TYPES.TSTypeReference:
return (
// Ignore `as const` and `<const>`
!isConst(node) ||
// Allow qualified names which have dots between identifiers, `Foo.Bar`
node.typeName.type === AST_NODE_TYPES.TSQualifiedName
);
default:
return true;
}
}
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)
);
}
function checkExpressionForObjectAssertion(
node: AsExpressionOrTypeAssertion,
): void {
if (
options.assertionStyle === 'never' ||
options.objectLiteralTypeAssertions === 'allow' ||
node.expression.type !== AST_NODE_TYPES.ObjectExpression
) {
return;
}
if (
options.objectLiteralTypeAssertions === 'allow-as-parameter' &&
isAsParameter(node)
) {
return;
}
if (checkType(node.typeAnnotation)) {
const suggest = getSuggestions(
node,
'replaceObjectTypeAssertionWithAnnotation',
'replaceObjectTypeAssertionWithSatisfies',
);
context.report({
node,
messageId: 'unexpectedObjectTypeAssertion',
suggest,
});
}
}
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)
) {
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);
},
};
}
Internal helpers¶
Declared inside another function in this file.
isConst(node: TSESTree.TypeNode): boolean¶
Parameters:
nodeTSESTree.TypeNode
Returns: boolean
Code
reportIncorrectAssertionType(node: AsExpressionOrTypeAssertion): void¶
Parameters:
nodeAsExpressionOrTypeAssertion
Returns: void
Calls:
isConstcontext.reportcontext.sourceCode.getTextgetParserServices( context, true, ).esTreeNodeToTSNodeMap.getgetOperatorPrecedence (from ../util)ts.isBinaryExpressionts.isNewExpressiongetOperatorPrecedenceForNode (from ../util)getWrappedCode (from ../util/getWrappedCode)fixer.replaceTextisParenthesized (from ../util)
Internal Comments:
// If this node is `as const`, then don't report an error.
// lazily access parserServices to avoid crashing on non TS files (#9860) (x2)
Code
function reportIncorrectAssertionType(
node: AsExpressionOrTypeAssertion,
): void {
const messageId = options.assertionStyle;
// If this node is `as const`, then don't report an error.
if (isConst(node.typeAnnotation) && messageId === 'never') {
return;
}
context.report({
node,
messageId,
data:
messageId !== 'never'
? { cast: context.sourceCode.getText(node.typeAnnotation) }
: {},
fix:
messageId === 'as'
? (fixer): TSESLint.RuleFix => {
// lazily access parserServices to avoid crashing on non TS files (#9860)
const tsNode = getParserServices(
context,
true,
).esTreeNodeToTSNodeMap.get(node as TSESTree.TSTypeAssertion);
const expressionCode = context.sourceCode.getText(
node.expression,
);
const typeAnnotationCode = context.sourceCode.getText(
node.typeAnnotation,
);
const asPrecedence = getOperatorPrecedence(
ts.SyntaxKind.AsExpression,
ts.SyntaxKind.Unknown,
);
const parentPrecedence = getOperatorPrecedence(
tsNode.parent.kind,
ts.isBinaryExpression(tsNode.parent)
? tsNode.parent.operatorToken.kind
: ts.SyntaxKind.Unknown,
ts.isNewExpression(tsNode.parent)
? tsNode.parent.arguments != null &&
tsNode.parent.arguments.length > 0
: undefined,
);
const expressionPrecedence = getOperatorPrecedenceForNode(
node.expression,
);
const expressionCodeWrapped = getWrappedCode(
expressionCode,
expressionPrecedence,
asPrecedence,
);
const text = `${expressionCodeWrapped} as ${typeAnnotationCode}`;
return fixer.replaceText(
node,
isParenthesized(node, context.sourceCode)
? text
: getWrappedCode(text, asPrecedence, parentPrecedence),
);
}
: undefined,
});
}
checkType(node: TSESTree.TypeNode): boolean¶
Parameters:
nodeTSESTree.TypeNode
Returns: boolean
Calls:
isConst
Internal Comments:
// Ignore `as const` and `<const>` (x2)
// Allow qualified names which have dots between identifiers, `Foo.Bar` (x4)
Code
function checkType(node: TSESTree.TypeNode): boolean {
switch (node.type) {
case AST_NODE_TYPES.TSAnyKeyword:
case AST_NODE_TYPES.TSUnknownKeyword:
return false;
case AST_NODE_TYPES.TSTypeReference:
return (
// Ignore `as const` and `<const>`
!isConst(node) ||
// Allow qualified names which have dots between identifiers, `Foo.Bar`
node.typeName.type === AST_NODE_TYPES.TSQualifiedName
);
default:
return true;
}
}
getSuggestions(node: AsExpressionOrTypeAssertion, annotationMessageId: MessageIds, satisfiesMessageId: MessageIds): TSESLint.ReportSuggestionArray<MessageIds>¶
Parameters:
nodeAsExpressionOrTypeAssertionannotationMessageIdMessageIdssatisfiesMessageIdMessageIds
Returns: TSESLint.ReportSuggestionArray<MessageIds>
Calls:
suggestions.pushcontext.sourceCode.getTextfixer.insertTextAfterfixer.replaceTextgetTextWithParentheses (from ../util)
Code
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;
}
isAsParameter(node: AsExpressionOrTypeAssertion): boolean¶
Parameters:
nodeAsExpressionOrTypeAssertion
Returns: boolean
Code
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)
);
}
checkExpressionForObjectAssertion(node: AsExpressionOrTypeAssertion): void¶
Parameters:
nodeAsExpressionOrTypeAssertion
Returns: void
Calls:
isAsParametercheckTypegetSuggestionscontext.report
Code
function checkExpressionForObjectAssertion(
node: AsExpressionOrTypeAssertion,
): void {
if (
options.assertionStyle === 'never' ||
options.objectLiteralTypeAssertions === 'allow' ||
node.expression.type !== AST_NODE_TYPES.ObjectExpression
) {
return;
}
if (
options.objectLiteralTypeAssertions === 'allow-as-parameter' &&
isAsParameter(node)
) {
return;
}
if (checkType(node.typeAnnotation)) {
const suggest = getSuggestions(
node,
'replaceObjectTypeAssertionWithAnnotation',
'replaceObjectTypeAssertionWithSatisfies',
);
context.report({
node,
messageId: 'unexpectedObjectTypeAssertion',
suggest,
});
}
}
checkExpressionForArrayAssertion(node: AsExpressionOrTypeAssertion): void¶
Parameters:
nodeAsExpressionOrTypeAssertion
Returns: void
Calls:
isAsParametercheckTypegetSuggestionscontext.report
Code
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)
) {
return;
}
if (checkType(node.typeAnnotation)) {
const suggest = getSuggestions(
node,
'replaceArrayTypeAssertionWithAnnotation',
'replaceArrayTypeAssertionWithSatisfies',
);
context.report({
node,
messageId: 'unexpectedArrayTypeAssertion',
suggest,
});
}
}
Type Aliases¶
MessageIds¶
type MessageIds = | 'angle-bracket'
| 'as'
| 'never'
| 'replaceArrayTypeAssertionWithAnnotation'
| 'replaceArrayTypeAssertionWithSatisfies'
| 'replaceObjectTypeAssertionWithAnnotation'
| 'replaceObjectTypeAssertionWithSatisfies'
| 'unexpectedArrayTypeAssertion'
| 'unexpectedObjectTypeAssertion';
OptUnion¶
type OptUnion = | {
assertionStyle: 'angle-bracket' | 'as';
objectLiteralTypeAssertions?: 'allow' | 'allow-as-parameter' | 'never';
arrayLiteralTypeAssertions?: 'allow' | 'allow-as-parameter' | 'never';
}
| {
assertionStyle: 'never';
};
Options¶
AsExpressionOrTypeAssertion¶
Generated by Syntax Scribe