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
42 changes: 38 additions & 4 deletions crates/oxc_linter/src/rules/eslint/valid_typeof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn not_string(help: Option<&'static str>, span: Span) -> OxcDiagnostic {
d
}

fn invalid_value(help: Option<&'static str>, span: Span) -> OxcDiagnostic {
fn invalid_value(help: Option<String>, span: Span) -> OxcDiagnostic {
let mut d = OxcDiagnostic::warn("Invalid `typeof` comparison value.").with_label(span);
if let Some(x) = help {
d = d.with_help(x);
Expand Down Expand Up @@ -123,7 +123,9 @@ impl Rule for ValidTypeof {

if let Expression::StringLiteral(lit) = sibling {
if !VALID_TYPES.contains(&lit.value.as_str()) {
ctx.diagnostic(invalid_value(None, sibling.span()));
let help = get_typo_suggestion(lit.value.as_str())
.map(|suggestion| format!("Did you mean `\"{suggestion}\"`?"));
ctx.diagnostic(invalid_value(help, sibling.span()));
}
return;
}
Expand All @@ -132,7 +134,9 @@ impl Rule for ValidTypeof {
&& let Some(quasi) = template.single_quasi()
{
if !VALID_TYPES.contains(&quasi.as_str()) {
ctx.diagnostic(invalid_value(None, sibling.span()));
let help = get_typo_suggestion(quasi.as_str())
.map(|suggestion| format!("Did you mean `\"{suggestion}\"`?"));
ctx.diagnostic(invalid_value(help, sibling.span()));
}
return;
}
Expand All @@ -146,7 +150,7 @@ impl Rule for ValidTypeof {
not_string(Some("Use `\"undefined\"` instead of `undefined`."), sibling.span())
} else {
invalid_value(
Some("Use `\"undefined\"` instead of `undefined`."),
Some("Use `\"undefined\"` instead of `undefined`.".to_string()),
sibling.span(),
)
},
Expand All @@ -166,6 +170,29 @@ impl Rule for ValidTypeof {
const VALID_TYPES: [&str; 8] =
["bigint", "boolean", "function", "number", "object", "string", "symbol", "undefined"];

/// Check for common misspellings of typeof values and return a suggestion.
fn get_typo_suggestion(value: &str) -> Option<&'static str> {
// spellchecker:off
match value {
"strnig" | "stirng" | "sting" | "strng" | "srting" | "srtring" | "strning" => {
Some("string")
}
"umdefined" | "undefimed" | "underfined" | "undefinied" | "undefied" | "undeffined"
| "undefind" | "undifined" | "udefined" | "undfined" => Some("undefined"),
"fucntion" | "funtion" | "fuction" | "functon" | "funcion" | "funciton" | "functin"
| "funcitn" | "funcrion" => Some("function"),
"obejct" | "objcet" | "obect" | "objetc" | "objetct" | "objet" | "objec" => Some("object"),
"bolean" | "booleen" | "boolen" | "boolaen" | "booelan" | "bollean" => Some("boolean"),
"nunber" | "nubmer" | "numbre" | "numver" | "numbr" | "numebr" | "nmber" => Some("number"),
"symol" | "symblo" | "simbole" | "synbol" | "symboll" | "symbal" => Some("symbol"),
"biigint" | "bignt" | "biignt" | "bigimit" | "bignit" | "bigit" | "begint" => {
Some("bigint")
}
_ => None,
}
// spellchecker:on
}

#[test]
fn test() {
use crate::tester::Tester;
Expand Down Expand Up @@ -240,6 +267,13 @@ fn test() {
"typeof foo === `${string}`",
Some(serde_json::json!([{ "requireStringLiterals": true }])),
),
// Typo suggestions for each valid typeof type
("typeof foo === 'biigint'", None), // spellchecker:disable-line
("typeof foo === 'bolean'", None), // spellchecker:disable-line
("typeof foo === 'fucntion'", None), // spellchecker:disable-line
("typeof foo === 'nunber'", None), // spellchecker:disable-line
("typeof foo === 'obejct'", None), // spellchecker:disable-line
("typeof foo === 'symol'", None), // spellchecker:disable-line
];

let fix = vec![("typeof foo === undefined", r#"typeof foo === "undefined""#)];
Expand Down
55 changes: 55 additions & 0 deletions crates/oxc_linter/src/snapshots/eslint_valid_typeof.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,78 +6,91 @@ source: crates/oxc_linter/src/tester.rs
1 │ typeof foo === 'strnig'
· ────────
╰────
help: Did you mean `"string"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:1]
1 │ 'strnig' === typeof foo
· ────────
╰────
help: Did you mean `"string"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:20]
1 │ if (typeof bar === 'umdefined') {}
· ───────────
╰────
help: Did you mean `"undefined"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:16]
1 │ typeof foo !== 'strnig'
· ────────
╰────
help: Did you mean `"string"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:1]
1 │ 'strnig' !== typeof foo
· ────────
╰────
help: Did you mean `"string"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:20]
1 │ if (typeof bar !== 'umdefined') {}
· ───────────
╰────
help: Did you mean `"undefined"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:15]
1 │ typeof foo != 'strnig'
· ────────
╰────
help: Did you mean `"string"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:1]
1 │ 'strnig' != typeof foo
· ────────
╰────
help: Did you mean `"string"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:19]
1 │ if (typeof bar != 'umdefined') {}
· ───────────
╰────
help: Did you mean `"undefined"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:15]
1 │ typeof foo == 'strnig'
· ────────
╰────
help: Did you mean `"string"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:1]
1 │ 'strnig' == typeof foo
· ────────
╰────
help: Did you mean `"string"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:19]
1 │ if (typeof bar == 'umdefined') {}
· ───────────
╰────
help: Did you mean `"undefined"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:20]
1 │ if (typeof bar === `umdefined`) {}
· ───────────
╰────
help: Did you mean `"undefined"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:15]
Expand Down Expand Up @@ -130,3 +143,45 @@ source: crates/oxc_linter/src/tester.rs
1 │ typeof foo === `${string}`
· ───────────
╰────

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:16]
1 │ typeof foo === 'biigint'
· ─────────
╰────
help: Did you mean `"bigint"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:16]
1 │ typeof foo === 'bolean'
· ────────
╰────
help: Did you mean `"boolean"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:16]
1 │ typeof foo === 'fucntion'
· ──────────
╰────
help: Did you mean `"function"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:16]
1 │ typeof foo === 'nunber'
· ────────
╰────
help: Did you mean `"number"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:16]
1 │ typeof foo === 'obejct'
· ────────
╰────
help: Did you mean `"object"`?

⚠ eslint(valid-typeof): Invalid `typeof` comparison value.
╭─[valid_typeof.tsx:1:16]
1 │ typeof foo === 'symol'
· ───────
╰────
help: Did you mean `"symbol"`?
Loading