Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 88 additions & 6 deletions crates/oxc_linter/src/rules/typescript/no_invalid_void_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,10 @@ impl Rule for NoInvalidVoidType {

if let AstKind::TSTypeParameterInstantiation(_) = parent.kind() {
let grand_parent_node = ctx.nodes().parent_node(parent.id());
if let AstKind::TSTypeReference(type_reference) = grand_parent_node.kind() {
self.check_generic_type_argument(
keyword.span,
ctx.source_range(type_reference.type_name.span()),
ctx,
);
if let Some(fully_qualified_name) =
get_generic_type_argument_name(parent.kind().span(), grand_parent_node.kind(), ctx)
{
self.check_generic_type_argument(keyword.span, fully_qualified_name, ctx);
return;
}

Expand Down Expand Up @@ -267,6 +265,32 @@ impl NoInvalidVoidType {
}
}

fn get_generic_type_argument_name<'a>(
type_arguments_span: Span,
grand_parent: AstKind<'a>,
ctx: &LintContext<'a>,
) -> Option<&'a str> {
match grand_parent {
AstKind::TSTypeReference(type_reference) => {
Some(ctx.source_range(type_reference.type_name.span()))
}
AstKind::TSClassImplements(class_implements) => {
Some(ctx.source_range(class_implements.expression.span()))
}
AstKind::TSInterfaceHeritage(interface_heritage) => {
Some(ctx.source_range(interface_heritage.expression.span()))
}
AstKind::Class(class) => class
.super_type_arguments
.as_ref()
.filter(|type_arguments| type_arguments.span == type_arguments_span)
.and_then(|_| {
class.super_class.as_ref().map(|super_class| ctx.source_range(super_class.span()))
}),
_ => None,
}
}

fn is_valid_return_type_annotation(parent: AstKind<'_>, grand_parent: Option<AstKind<'_>>) -> bool {
let AstKind::TSTypeAnnotation(type_annotation) = parent else {
return false;
Expand Down Expand Up @@ -829,6 +853,32 @@ fn test() {
",
Some(serde_json::json!([{ "allowAsThisParameter": true }])),
),
(
"
interface Producer<T> {
get: () => T;
}

export class Test implements Producer<void> {
get = () => null;
}
",
None,
),
("class Test extends Base<void> {}", None),
("interface Test extends Base<void> {}", None),
(
"
interface Producer<T> {
get: () => T;
}

export class Test implements Producer<void> {
get = () => null;
}
",
Some(serde_json::json!([{ "allowInGenericTypeArguments": ["Producer"] }])),
),
];

let fail = vec![
Expand Down Expand Up @@ -973,6 +1023,18 @@ fn test() {
"type BannedVoid = Ex.Mx.Tx<void>;",
Some(serde_json::json!([{ "allowInGenericTypeArguments": ["Tx"] }])),
),
(
"
interface Producer<T> {
get: () => T;
}

export class Test implements Producer<void> {
get = () => null;
}
",
Some(serde_json::json!([{ "allowInGenericTypeArguments": ["Base"] }])),
),
(
"function takeVoid(thing: void) {}",
Some(serde_json::json!([{ "allowInGenericTypeArguments": ["Allowed"] }])),
Expand All @@ -995,6 +1057,26 @@ fn test() {
serde_json::json!([ { "allowAsThisParameter": true, "allowInGenericTypeArguments": false }, ]),
),
),
(
"
interface Producer<T> {
get: () => T;
}

export class Test implements Producer<void> {
get = () => null;
}
",
Some(serde_json::json!([{ "allowInGenericTypeArguments": false }])),
),
(
"class Test extends Base<void> {}",
Some(serde_json::json!([{ "allowInGenericTypeArguments": false }])),
),
(
"interface Test extends Base<void> {}",
Some(serde_json::json!([{ "allowInGenericTypeArguments": false }])),
),
];

Tester::new(NoInvalidVoidType::NAME, NoInvalidVoidType::PLUGIN, pass, fail).test_and_snapshot();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,15 @@ source: crates/oxc_linter/src/tester.rs
╰────
help: Replace this `void` type with an allowed type, or keep `void` only in a valid return position.

⚠ typescript-eslint(no-invalid-void-type): Do not use `void` as a type argument for `Producer`.
╭─[no_invalid_void_type.tsx:6:51]
5 │
6 │ export class Test implements Producer<void> {
· ────
7 │ get = () => null;
╰────
help: Replace this `void` type with an allowed type, or keep `void` only in a valid return position.

⚠ typescript-eslint(no-invalid-void-type): Use `void` only as a return type or generic type argument.
╭─[no_invalid_void_type.tsx:1:26]
1 │ function takeVoid(thing: void) {}
Expand Down Expand Up @@ -354,3 +363,26 @@ source: crates/oxc_linter/src/tester.rs
· ────
╰────
help: Replace this `void` type with an allowed type, or keep `void` only in a valid return position.

⚠ typescript-eslint(no-invalid-void-type): Use `void` only as a return type.
╭─[no_invalid_void_type.tsx:6:51]
5 │
6 │ export class Test implements Producer<void> {
· ────
7 │ get = () => null;
╰────
help: Replace this `void` type with an allowed type, or keep `void` only in a valid return position.

⚠ typescript-eslint(no-invalid-void-type): Use `void` only as a return type.
╭─[no_invalid_void_type.tsx:1:25]
1 │ class Test extends Base<void> {}
· ────
╰────
help: Replace this `void` type with an allowed type, or keep `void` only in a valid return position.

⚠ typescript-eslint(no-invalid-void-type): Use `void` only as a return type.
╭─[no_invalid_void_type.tsx:1:29]
1 │ interface Test extends Base<void> {}
· ────
╰────
help: Replace this `void` type with an allowed type, or keep `void` only in a valid return position.
Loading