diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index b4e804c658d1e..16934717d6144 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -168,6 +168,7 @@ mod typescript { pub mod no_this_alias; pub mod no_unnecessary_type_constraint; pub mod no_unsafe_declaration_merging; + pub mod no_unsafe_function_type; pub mod no_useless_empty_export; pub mod no_var_requires; pub mod no_wrapper_object_types; @@ -842,6 +843,7 @@ oxc_macros::declare_all_lint_rules! { typescript::no_this_alias, typescript::no_unnecessary_type_constraint, typescript::no_unsafe_declaration_merging, + typescript::no_unsafe_function_type, typescript::no_useless_empty_export, typescript::no_var_requires, typescript::no_wrapper_object_types, diff --git a/crates/oxc_linter/src/rules/typescript/no_unsafe_function_type.rs b/crates/oxc_linter/src/rules/typescript/no_unsafe_function_type.rs new file mode 100644 index 0000000000000..791fb5f52979b --- /dev/null +++ b/crates/oxc_linter/src/rules/typescript/no_unsafe_function_type.rs @@ -0,0 +1,121 @@ +use oxc_ast::{ + ast::{Expression, IdentifierReference, TSTypeName}, + AstKind, +}; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_semantic::IsGlobalReference; +use oxc_span::Span; + +use crate::{context::LintContext, rule::Rule, AstNode}; + +fn no_unsafe_function_type_diagnostic(span: Span) -> OxcDiagnostic { + // See for details + OxcDiagnostic::warn("The `Function` type accepts any function-like value.") + .with_help("Prefer explicitly defining any function parameters and return type.") + .with_label(span) +} + +#[derive(Debug, Default, Clone)] +pub struct NoUnsafeFunctionType; + +declare_oxc_lint!( + /// ### What it does + /// Disallow using the unsafe built-in Function type. + /// + /// ### Why is this bad? + /// TypeScript's built-in Function type allows being called with any number of arguments and returns type any. Function also allows classes or plain objects that happen to possess all properties of the Function class. It's generally better to specify function parameters and return types with the function type syntax. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```ts + /// let noParametersOrReturn: Function; + /// noParametersOrReturn = () => {}; + /// + /// let stringToNumber: Function; + /// stringToNumber = (text: string) => text.length; + /// + /// let identity: Function; + /// identity = value => value; + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```ts + /// let noParametersOrReturn: () => void; + /// noParametersOrReturn = () => {}; + /// + /// let stringToNumber: (text: string) => number; + /// stringToNumber = text => text.length; + /// + /// let identity: (value: T) => T; + /// identity = value => value; + /// ``` + NoUnsafeFunctionType, + pedantic, +); + +impl Rule for NoUnsafeFunctionType { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + match node.kind() { + AstKind::TSTypeReference(reference) => { + if let TSTypeName::IdentifierReference(ident_ref) = &reference.type_name { + handle_function_type(ident_ref, ctx); + } + } + AstKind::TSClassImplements(implements) => { + if let TSTypeName::IdentifierReference(ident_ref) = &implements.expression { + handle_function_type(ident_ref, ctx); + } + } + AstKind::TSInterfaceHeritage(heritage) => { + if let Expression::Identifier(ident) = &heritage.expression { + handle_function_type(ident, ctx); + } + } + _ => {} + } + } +} + +fn handle_function_type<'a>(identifier: &'a IdentifierReference<'a>, ctx: &LintContext<'a>) { + if identifier.name == "Function" && identifier.is_global_reference(ctx.symbols()) { + ctx.diagnostic(no_unsafe_function_type_diagnostic(identifier.span)); + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + "let value: () => void;", + "let value: (t: T) => T;", + " + // create a scope since it's illegal to declare a duplicate identifier + // 'Function' in the global script scope. + { + type Function = () => void; + let value: Function; + } + ", + ]; + + let fail = vec![ + "let value: Function;", + "let value: Function[];", + "let value: Function | number;", + " + class Weird implements Function { + // ... + } + ", + " + interface Weird extends Function { + // ... + } + ", + ]; + + Tester::new(NoUnsafeFunctionType::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/no_unsafe_function_type.snap b/crates/oxc_linter/src/snapshots/no_unsafe_function_type.snap new file mode 100644 index 0000000000000..60c3cfc614db7 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_unsafe_function_type.snap @@ -0,0 +1,41 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + ⚠ typescript-eslint(no-unsafe-function-type): The `Function` type accepts any function-like value. + ╭─[no_unsafe_function_type.tsx:1:12] + 1 │ let value: Function; + · ──────── + ╰──── + help: Prefer explicitly defining any function parameters and return type. + + ⚠ typescript-eslint(no-unsafe-function-type): The `Function` type accepts any function-like value. + ╭─[no_unsafe_function_type.tsx:1:12] + 1 │ let value: Function[]; + · ──────── + ╰──── + help: Prefer explicitly defining any function parameters and return type. + + ⚠ typescript-eslint(no-unsafe-function-type): The `Function` type accepts any function-like value. + ╭─[no_unsafe_function_type.tsx:1:12] + 1 │ let value: Function | number; + · ──────── + ╰──── + help: Prefer explicitly defining any function parameters and return type. + + ⚠ typescript-eslint(no-unsafe-function-type): The `Function` type accepts any function-like value. + ╭─[no_unsafe_function_type.tsx:2:35] + 1 │ + 2 │ class Weird implements Function { + · ──────── + 3 │ // ... + ╰──── + help: Prefer explicitly defining any function parameters and return type. + + ⚠ typescript-eslint(no-unsafe-function-type): The `Function` type accepts any function-like value. + ╭─[no_unsafe_function_type.tsx:2:36] + 1 │ + 2 │ interface Weird extends Function { + · ──────── + 3 │ // ... + ╰──── + help: Prefer explicitly defining any function parameters and return type.