perf(linter/typescript/no-wrapper-object-types): index references (#28359)

Look up six wrapper names once per TypeScript file, preserving
shadowing, type contexts, and heritage fix restrictions. Replace 13,199
fixture callbacks with four file callbacks plus matching-reference
iteration.


---

<sub>Stack created with <a
href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a
href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Cam McHenryandCopilot authored and GitHub committed 2026-10-28 22:28:47 -04:00
1 parent dbd8a0b7f1
commit b102a91b15
4 files changed
+46 -47

No files matched your search

+2 -6
View File
@@ -2031,12 +2031,8 @@ impl RuleRunner for crate::rules::typescript::no_var_requires::NoVarRequires {
}
impl RuleRunner for crate::rules::typescript::no_wrapper_object_types::NoWrapperObjectTypes {
const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[
AstType::TSClassImplements,
AstType::TSInterfaceHeritage,
AstType::TSTypeReference,
]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
const NODE_TYPES: Option<&AstTypesBitset> = None;
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::RunOnce;
}
impl RuleRunner
@@ -1,10 +1,10 @@
use cow_utils::CowUtils;
use oxc_ast::{AstKind, ast::TSTypeName};
use oxc_ast::AstKind;
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{AstNode, context::LintContext, rule::Rule};
use crate::{context::LintContext, rule::Rule};
fn no_wrapper_object_types(ident_name: &str, span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Do not use wrapper object types.")
@@ -60,48 +60,35 @@ declare_oxc_lint!(
);
impl Rule for NoWrapperObjectTypes {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let (ident_name, ident_span, reference_id) = match node.kind() {
AstKind::TSTypeReference(type_ref) => {
if let TSTypeName::IdentifierReference(type_name) = &type_ref.type_name {
(type_name.name.as_str(), type_name.span, type_name.reference_id())
} else {
return;
fn run_once(&self, ctx: &LintContext) {
let scoping = ctx.scoping();
for name in ["BigInt", "Boolean", "Number", "Object", "String", "Symbol"] {
let Some(reference_ids) = scoping.root_unresolved_references().get(name) else {
continue;
};
for &reference_id in reference_ids {
let reference = scoping.get_reference(reference_id);
if reference.symbol_id().is_some() {
continue;
}
}
AstKind::TSClassImplements(ts_class_implements) => {
if let TSTypeName::IdentifierReference(type_name) = &ts_class_implements.expression
{
(type_name.name.as_str(), type_name.span, type_name.reference_id())
let node = ctx.nodes().get_node(reference.node_id());
let AstKind::IdentifierReference(ident) = node.kind() else {
continue;
};
let can_fix = match ctx.nodes().parent_kind(node.id()) {
AstKind::TSTypeReference(_) => true,
AstKind::TSClassImplements(_) | AstKind::TSInterfaceHeritage(_) => false,
_ => continue,
};
if can_fix {
ctx.diagnostic_with_fix(no_wrapper_object_types(name, ident.span), |fixer| {
fixer.replace(ident.span, name.cow_to_ascii_lowercase().to_string())
});
} else {
return;
ctx.diagnostic(no_wrapper_object_types(name, ident.span));
}
}
AstKind::TSInterfaceHeritage(ts_interface_heritage) => {
if let TSTypeName::IdentifierReference(type_name) = &ts_interface_heritage.type_name
{
(type_name.name.as_str(), type_name.span, type_name.reference_id())
} else {
return;
}
}
_ => return,
};
if matches!(ident_name, "BigInt" | "Boolean" | "Number" | "Object" | "String" | "Symbol") {
if ctx.scoping().get_reference(reference_id).symbol_id().is_some() {
return;
}
let can_fix = matches!(node.kind(), AstKind::TSTypeReference(_));
if can_fix {
ctx.diagnostic_with_fix(no_wrapper_object_types(ident_name, ident_span), |fixer| {
fixer.replace(ident_span, ident_name.cow_to_ascii_lowercase().to_string())
});
} else {
ctx.diagnostic(no_wrapper_object_types(ident_name, ident_span));
}
}
}
@@ -156,6 +143,11 @@ fn test() {
"type Unknown = {};",
"type Void = {};",
"class MyClass extends Number {}",
"const value = Number(1); const object = new Object();",
"type Constructor = typeof Number;",
"let value: Number.Member;",
"interface Value extends Namespace.Number {}",
"class Value implements Namespace.Number {}",
"
type Number = 0 | 1;
let value: Number;
@@ -185,6 +177,7 @@ fn test() {
"class MyClass implements Number {}",
"interface MyInterface extends Number {}",
"type MyType = Number & String;",
"let globalValue: Number; function f() { type Number = string; let localValue: Number; }",
];
let fix = vec![
@@ -200,6 +193,8 @@ fn test() {
("type MyType = Number;", "type MyType = number;"),
("type MyType = [Number];", "type MyType = [number];"),
("type MyType = Number & String;", "type MyType = number & string;"),
("class Value implements Number {}", "class Value implements Number {}"),
("interface Value extends Number {}", "interface Value extends Number {}"),
];
Tester::new(NoWrapperObjectTypes::NAME, NoWrapperObjectTypes::PLUGIN, pass, fail)
@@ -127,3 +127,11 @@ source: crates/oxc_linter/src/tester.rs
╰────
help: Replace `String` with `string`.
note: `String` is a boxed object type, not a primitive. Boxed types have object semantics (identity/truthiness) that can be surprising. Use `string` for values, and in `extends`/`implements` use an interface/object shape instead.
⚠ typescript(no-wrapper-object-types): Do not use wrapper object types.
╭─[no_wrapper_object_types.tsx:1:18]
1 │ let globalValue: Number; function f() { type Number = string; let localValue: Number; }
· ──────
╰────
help: Replace `Number` with `number`.
note: `Number` is a boxed object type, not a primitive. Boxed types have object semantics (identity/truthiness) that can be surprising. Use `number` for values, and in `extends`/`implements` use an interface/object shape instead.
@@ -500,7 +500,7 @@ typescript/no-unsafe-declaration-merging | 500
typescript/no-unsafe-function-type | 13199
typescript/no-useless-empty-export | 5
typescript/no-var-requires | 62606
typescript/no-wrapper-object-types | 13199
typescript/no-wrapper-object-types | 4
typescript/parameter-properties | 1070
typescript/prefer-as-const | 1054
typescript/prefer-enum-initializers | 23