📄 no-invalid-void-type¶
📊 Analysis Summary¶
| Metric | Count |
|---|---|
| 🔧 Functions | 6 |
| 📦 Imports | 4 |
| 📐 Interfaces | 1 |
| 📑 Type Aliases | 1 |
📚 Table of Contents¶
🛠️ File Location:¶
📂 packages/eslint-plugin/src/rules/no-invalid-void-type.ts
📤 Default Export¶
| Property | Value |
|---|---|
name |
'no-invalid-void-type' |
meta.type |
'problem' |
meta.docs.description |
'Disallow void type outside of generic or return types' |
meta.docs.recommended |
'strict' |
meta.messages.invalidVoidForGeneric |
'{{ generic }} may not have void as a type argument.' |
meta.messages.invalidVoidNotReturn |
'void is only valid as a return type.' |
meta.messages.invalidVoidNotReturnOrGeneric |
'void is only valid as a return type or generic type argument.' |
meta.messages.invalidVoidNotReturnOrThisParam |
'void is only valid as return type or type of this parameter.' |
meta.messages.invalidVoidNotReturnOrThisParamOrGeneric |
'void is only valid as a return type or generic type argument or the type of a this parameter.' |
meta.messages.invalidVoidUnionConstituent |
'void is not valid as a constituent in a union type' |
meta.schema |
[ { type: 'object', additionalProperties: false, properties: { allowAsThisParameter: { type: 'boolean', description: ... |
defaultOptions |
[ { allowAsThisParameter: false, allowInGenericTypeArguments: true }, ] |
Entry point: create — documented under Functions.
📦 Imports¶
| Name | Source |
|---|---|
TSESTree |
@typescript-eslint/utils |
AST_NODE_TYPES |
@typescript-eslint/utils |
createRule |
../util |
hasOverloadSignatures |
../util |
Functions¶
create(context: any, [{ allowAsThisParameter, allowI…: any): { TSVoidKeyword(node: TSESTree.TSVoidKeyword): void; }¶
Parameters:
contextany[{ allowAsThisParameter, allowInGenericTypeArguments }]any
Returns: { TSVoidKeyword(node: TSESTree.TSVoidKeyword): void; }
Calls:
validParents.pushArray.isArraycontext.sourceCode .getText(node.parent.parent.typeName) .replaceAllallowInGenericTypeArguments .map(s => s.replaceAll(' ', '')) .includescontext.reportgetNotReturnOrGenericMessageIdnode.types.everyvalidUnionMembers.includesmember.typeArguments.params .map(param => param.type) .includescheckGenericTypeArgumentcheckDefaultVoidisValidUnionTypegetParentFunctionDeclarationNodehasOverloadSignatures (from ../util)validParents.includesinvalidGrandParents.includes
Internal Comments:
/**
* @brief check if the given void keyword is used as a valid generic type
*
* reports if the type parametrized by void is not in the allowlist, or
* allowInGenericTypeArguments is false.
* no-op if the given void keyword is not used as generic type
*/
// only matches T<..., void, ...>
// extra check for precaution
/* istanbul ignore next */
// check allowlist
/**
* @brief checks if the generic type parameter defaults to void
*/
/**
* @brief checks that a union containing void is valid
* @return true if every member of the union is specified as a valid type in
* validUnionMembers, or is a valid generic type parametrized by void
*/
// allows any T<..., void, ...> here, checked by checkGenericTypeArgument
// checks T<..., void, ...> against specification of allowInGenericArguments option
// allow <T = void> if allowInGenericTypeArguments is specified, and report if the generic type parameter extends void
// union w/ void must contain types from validUnionMembers, or a valid generic void type
// using `void` as part of the return type of function overloading implementation
// this parameter is ok to be void.
// default cases
// https://github.com/typescript-eslint/typescript-eslint/issues/6225
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
Code
create(context, [{ allowAsThisParameter, allowInGenericTypeArguments }]) {
const validParents: AST_NODE_TYPES[] = [
AST_NODE_TYPES.TSTypeAnnotation, //
];
const invalidGrandParents: AST_NODE_TYPES[] = [
AST_NODE_TYPES.TSPropertySignature,
AST_NODE_TYPES.CallExpression,
AST_NODE_TYPES.PropertyDefinition,
AST_NODE_TYPES.AccessorProperty,
AST_NODE_TYPES.Identifier,
];
const validUnionMembers: AST_NODE_TYPES[] = [
AST_NODE_TYPES.TSVoidKeyword,
AST_NODE_TYPES.TSNeverKeyword,
];
if (allowInGenericTypeArguments === true) {
validParents.push(AST_NODE_TYPES.TSTypeParameterInstantiation);
}
/**
* @brief check if the given void keyword is used as a valid generic type
*
* reports if the type parametrized by void is not in the allowlist, or
* allowInGenericTypeArguments is false.
* no-op if the given void keyword is not used as generic type
*/
function checkGenericTypeArgument(node: TSESTree.TSVoidKeyword): void {
// only matches T<..., void, ...>
// extra check for precaution
/* istanbul ignore next */
if (
node.parent.type !== AST_NODE_TYPES.TSTypeParameterInstantiation ||
node.parent.parent.type !== AST_NODE_TYPES.TSTypeReference
) {
return;
}
// check allowlist
if (Array.isArray(allowInGenericTypeArguments)) {
const fullyQualifiedName = context.sourceCode
.getText(node.parent.parent.typeName)
.replaceAll(' ', '');
if (
!allowInGenericTypeArguments
.map(s => s.replaceAll(' ', ''))
.includes(fullyQualifiedName)
) {
context.report({
node,
messageId: 'invalidVoidForGeneric',
data: { generic: fullyQualifiedName },
});
}
return;
}
if (!allowInGenericTypeArguments) {
context.report({
node,
messageId: allowAsThisParameter
? 'invalidVoidNotReturnOrThisParam'
: 'invalidVoidNotReturn',
});
}
}
/**
* @brief checks if the generic type parameter defaults to void
*/
function checkDefaultVoid(
node: TSESTree.TSVoidKeyword,
parentNode: TSESTree.TSTypeParameter,
): void {
if (parentNode.default !== node) {
context.report({
node,
messageId: getNotReturnOrGenericMessageId(node),
});
}
}
/**
* @brief checks that a union containing void is valid
* @return true if every member of the union is specified as a valid type in
* validUnionMembers, or is a valid generic type parametrized by void
*/
function isValidUnionType(node: TSESTree.TSUnionType): boolean {
return node.types.every(
member =>
validUnionMembers.includes(member.type) ||
// allows any T<..., void, ...> here, checked by checkGenericTypeArgument
(member.type === AST_NODE_TYPES.TSTypeReference &&
member.typeArguments?.type ===
AST_NODE_TYPES.TSTypeParameterInstantiation &&
member.typeArguments.params
.map(param => param.type)
.includes(AST_NODE_TYPES.TSVoidKeyword)),
);
}
return {
TSVoidKeyword(node: TSESTree.TSVoidKeyword): void {
// checks T<..., void, ...> against specification of allowInGenericArguments option
if (
node.parent.type === AST_NODE_TYPES.TSTypeParameterInstantiation &&
node.parent.parent.type === AST_NODE_TYPES.TSTypeReference
) {
checkGenericTypeArgument(node);
return;
}
// allow <T = void> if allowInGenericTypeArguments is specified, and report if the generic type parameter extends void
if (
allowInGenericTypeArguments &&
node.parent.type === AST_NODE_TYPES.TSTypeParameter &&
node.parent.default?.type === AST_NODE_TYPES.TSVoidKeyword
) {
checkDefaultVoid(node, node.parent);
return;
}
// union w/ void must contain types from validUnionMembers, or a valid generic void type
if (
node.parent.type === AST_NODE_TYPES.TSUnionType &&
isValidUnionType(node.parent)
) {
return;
}
// using `void` as part of the return type of function overloading implementation
if (node.parent.type === AST_NODE_TYPES.TSUnionType) {
const declaringFunction = getParentFunctionDeclarationNode(
node.parent,
);
if (
declaringFunction &&
hasOverloadSignatures(declaringFunction, context)
) {
return;
}
}
// this parameter is ok to be void.
if (
allowAsThisParameter &&
node.parent.type === AST_NODE_TYPES.TSTypeAnnotation &&
node.parent.parent.type === AST_NODE_TYPES.Identifier &&
node.parent.parent.name === 'this'
) {
return;
}
// default cases
if (
validParents.includes(node.parent.type) &&
// https://github.com/typescript-eslint/typescript-eslint/issues/6225
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
!invalidGrandParents.includes(node.parent.parent!.type)
) {
return;
}
context.report({
node,
messageId:
allowInGenericTypeArguments && allowAsThisParameter
? 'invalidVoidNotReturnOrThisParamOrGeneric'
: allowInGenericTypeArguments
? getNotReturnOrGenericMessageId(node)
: allowAsThisParameter
? 'invalidVoidNotReturnOrThisParam'
: 'invalidVoidNotReturn',
});
},
};
}
getNotReturnOrGenericMessageId(node: TSESTree.TSVoidKeyword): MessageIds¶
Parameters:
nodeTSESTree.TSVoidKeyword
Returns: MessageIds
Code
getParentFunctionDeclarationNode(node: TSESTree.Node): TSESTree.FunctionDeclaration | TSESTree.MethodDefinition | …¶
Parameters:
nodeTSESTree.Node
Returns: TSESTree.FunctionDeclaration | TSESTree.MethodDefinition | null
Code
function getParentFunctionDeclarationNode(
node: TSESTree.Node,
): TSESTree.FunctionDeclaration | TSESTree.MethodDefinition | null {
let current = node.parent;
while (current) {
if (current.type === AST_NODE_TYPES.FunctionDeclaration) {
return current;
}
if (
current.type === AST_NODE_TYPES.MethodDefinition &&
current.value.body != null
) {
return current;
}
current = current.parent;
}
return null;
}
Internal helpers¶
Declared inside another function in this file.
checkGenericTypeArgument(node: TSESTree.TSVoidKeyword): void¶
Tags: @brief check if the given void keyword is used as a valid generic type
reports if the type parametrized by void is not in the allowlist, or allowInGenericTypeArguments is false. no-op if the given void keyword is not used as generic type
Raw JSDoc
Calls:
Array.isArraycontext.sourceCode .getText(node.parent.parent.typeName) .replaceAllallowInGenericTypeArguments .map(s => s.replaceAll(' ', '')) .includescontext.report
Internal Comments:
// only matches T<..., void, ...>
// extra check for precaution
/* istanbul ignore next */
// check allowlist
Code
function checkGenericTypeArgument(node: TSESTree.TSVoidKeyword): void {
// only matches T<..., void, ...>
// extra check for precaution
/* istanbul ignore next */
if (
node.parent.type !== AST_NODE_TYPES.TSTypeParameterInstantiation ||
node.parent.parent.type !== AST_NODE_TYPES.TSTypeReference
) {
return;
}
// check allowlist
if (Array.isArray(allowInGenericTypeArguments)) {
const fullyQualifiedName = context.sourceCode
.getText(node.parent.parent.typeName)
.replaceAll(' ', '');
if (
!allowInGenericTypeArguments
.map(s => s.replaceAll(' ', ''))
.includes(fullyQualifiedName)
) {
context.report({
node,
messageId: 'invalidVoidForGeneric',
data: { generic: fullyQualifiedName },
});
}
return;
}
if (!allowInGenericTypeArguments) {
context.report({
node,
messageId: allowAsThisParameter
? 'invalidVoidNotReturnOrThisParam'
: 'invalidVoidNotReturn',
});
}
}
checkDefaultVoid(node: TSESTree.TSVoidKeyword, parentNode: TSESTree.TSTypeParameter): void¶
Tags: @brief checks if the generic type parameter defaults to void
Calls:
context.reportgetNotReturnOrGenericMessageId
Code
isValidUnionType(node: TSESTree.TSUnionType): boolean¶
Returns: undefined
true if every member of the union is specified as a valid type in
validUnionMembers, or is a valid generic type parametrized by void
Tags: @brief checks that a union containing void is valid
Raw JSDoc
Calls:
node.types.everyvalidUnionMembers.includesmember.typeArguments.params .map(param => param.type) .includes
Internal Comments:
Code
function isValidUnionType(node: TSESTree.TSUnionType): boolean {
return node.types.every(
member =>
validUnionMembers.includes(member.type) ||
// allows any T<..., void, ...> here, checked by checkGenericTypeArgument
(member.type === AST_NODE_TYPES.TSTypeReference &&
member.typeArguments?.type ===
AST_NODE_TYPES.TSTypeParameterInstantiation &&
member.typeArguments.params
.map(param => param.type)
.includes(AST_NODE_TYPES.TSVoidKeyword)),
);
}
Interfaces¶
Options¶
Interface Code
Properties¶
| Name | Type | Optional | Description |
|---|---|---|---|
allowAsThisParameter |
boolean |
✓ | not shown |
allowInGenericTypeArguments |
boolean \| [string, ...string[]] |
✓ | not shown |
Type Aliases¶
MessageIds¶
type MessageIds = | 'invalidVoidForGeneric'
| 'invalidVoidNotReturn'
| 'invalidVoidNotReturnOrGeneric'
| 'invalidVoidNotReturnOrThisParam'
| 'invalidVoidNotReturnOrThisParamOrGeneric'
| 'invalidVoidUnionConstituent';
Generated by Syntax Scribe