Skip to content

⬅️ Back to Table of Contents

πŸ“„ no-unsafe-call

πŸ“Š Analysis Summary

Metric Count
πŸ”§ Functions 2
πŸ“¦ Imports 7
πŸ“‘ Type Aliases 1

πŸ“š Table of Contents

πŸ› οΈ File Location:

πŸ“‚ packages/eslint-plugin/src/rules/no-unsafe-call.ts

πŸ“€ Default Export

export default createRule<[], MessageIds>({ ... })
Property Value
name 'no-unsafe-call'
meta.type 'problem'
meta.docs.description 'Disallow calling a value with type any'
meta.docs.recommended 'recommended'
meta.docs.requiresTypeChecking true
meta.messages.errorCall 'Unsafe call of a type that could not be resolved.'
meta.messages.errorCallThis 'Unsafe call of a this type that could not be resolved.'
meta.messages.errorNew 'Unsafe construction of a type that could not be resolved.'
meta.messages.errorTemplateTag 'Unsafe use of a template tag whose type could not be resolved.'
meta.messages.unsafeCall 'Unsafe call of {{type}} typed value.'
meta.messages.unsafeCallThis [ 'Unsafe call of {{type}} typed value. this is typed as {{type}}.', 'You can try to fix this by turning on the `no...
meta.messages.unsafeNew 'Unsafe construction of {{type}} typed value.'
meta.messages.unsafeTemplateTag 'Unsafe use of {{type}} typed template tag.'
meta.schema []
defaultOptions []

Entry point: create β€” documented under Functions.


πŸ“¦ Imports

Name Source
TSESTree @typescript-eslint/utils
createRule ../util
getConstrainedTypeAtLocation ../util
getParserServices ../util
getThisExpression ../util
isBuiltinSymbolLike ../util
isTypeAnyType ../util

Functions

create(context: any): { 'CallExpression > *.callee'(node: TSESTree.CallExpression…

Parameters:

  • context any

Returns: { 'CallExpression > *.callee'(node: TSESTree.CallExpression["callee"]): void; NewExpression(node: any): void; 'TaggedTemplateExpression > *.tag'(node: TSESTree.Node): void; }

Calls:

  • getParserServices (from ../util)
  • services.program.getCompilerOptions
  • tsutils.isStrictCompilerOptionEnabled
  • getConstrainedTypeAtLocation (from ../util)
  • isTypeAnyType (from ../util)
  • getThisExpression (from ../util)
  • tsutils.isIntrinsicErrorType
  • context.report
  • isBuiltinSymbolLike (from ../util)
  • type.getConstructSignatures
  • type.getCallSignatures
  • callSignatures.some
  • tsutils.isIntrinsicVoidType
  • signature.getReturnType
  • checkCall

Internal Comments:

// `this()` or `this.foo()` or `this.foo[bar]()` (x2)
// this also matches subtypes of `Function`, like `interface Foo extends Function {}`. (x2)
// (x6)
// For weird TS reasons that I don't understand, these are (x2)
// safe to construct if: (x2)
// - they have at least one call signature _that is not void-returning_, (x2)
// - OR they have at least one construct signature. (x4)
// safe to call (including as template) if: (x2)
// - they have at least one call signature (x2)

Code
create(context) {
    const services = getParserServices(context);
    const compilerOptions = services.program.getCompilerOptions();
    const isNoImplicitThis = tsutils.isStrictCompilerOptionEnabled(
      compilerOptions,
      'noImplicitThis',
    );

    function checkCall(
      node: TSESTree.Node,
      reportingNode: TSESTree.Node,
      unsafeMessageId: Extract<MessageIds, `unsafe${string}`>,
      errorMessageId: Extract<MessageIds, `error${string}`>,
    ): void {
      const type = getConstrainedTypeAtLocation(services, node);

      if (isTypeAnyType(type)) {
        if (!isNoImplicitThis) {
          // `this()` or `this.foo()` or `this.foo[bar]()`
          const thisExpression = getThisExpression(node);
          if (
            thisExpression &&
            isTypeAnyType(
              getConstrainedTypeAtLocation(services, thisExpression),
            )
          ) {
            unsafeMessageId = 'unsafeCallThis';
            errorMessageId = 'errorCallThis';
          }
        }

        const isErrorType = tsutils.isIntrinsicErrorType(type);

        context.report({
          node: reportingNode,
          messageId: isErrorType ? errorMessageId : unsafeMessageId,
          data: {
            type: 'an `any`',
          },
        });
        return;
      }

      if (isBuiltinSymbolLike(services.program, type, 'Function')) {
        // this also matches subtypes of `Function`, like `interface Foo extends Function {}`.
        //
        // For weird TS reasons that I don't understand, these are
        //
        // safe to construct if:
        // - they have at least one call signature _that is not void-returning_,
        // - OR they have at least one construct signature.
        //
        // safe to call (including as template) if:
        // - they have at least one call signature
        // - OR they have at least one construct signature.

        const constructSignatures = type.getConstructSignatures();
        if (constructSignatures.length > 0) {
          return;
        }

        const callSignatures = type.getCallSignatures();
        if (unsafeMessageId === 'unsafeNew') {
          if (
            callSignatures.some(
              signature =>
                !tsutils.isIntrinsicVoidType(signature.getReturnType()),
            )
          ) {
            return;
          }
        } else if (callSignatures.length > 0) {
          return;
        }

        context.report({
          node: reportingNode,
          messageId: unsafeMessageId,
          data: {
            type: 'a `Function`',
          },
        });
        return;
      }
    }

    return {
      'CallExpression > *.callee'(
        node: TSESTree.CallExpression['callee'],
      ): void {
        checkCall(node, node, 'unsafeCall', 'errorCall');
      },
      NewExpression(node): void {
        checkCall(node.callee, node, 'unsafeNew', 'errorNew');
      },
      'TaggedTemplateExpression > *.tag'(node: TSESTree.Node): void {
        checkCall(node, node, 'unsafeTemplateTag', 'errorTemplateTag');
      },
    };
  }

Internal helpers

Declared inside another function in this file.

checkCall(node: TSESTree.Node, reportingNode: TSESTree.Node, unsafeMessageId: Extract<MessageIds,unsafe${string}>, errorMessageId: Extract<MessageIds,error${string}>): void

Parameters:

  • node TSESTree.Node
  • reportingNode TSESTree.Node
  • unsafeMessageId Extract<MessageIds,unsafe${string}>
  • errorMessageId Extract<MessageIds,error${string}>

Returns: void

Calls:

  • getConstrainedTypeAtLocation (from ../util)
  • isTypeAnyType (from ../util)
  • getThisExpression (from ../util)
  • tsutils.isIntrinsicErrorType
  • context.report
  • isBuiltinSymbolLike (from ../util)
  • type.getConstructSignatures
  • type.getCallSignatures
  • callSignatures.some
  • tsutils.isIntrinsicVoidType
  • signature.getReturnType

Internal Comments:

// `this()` or `this.foo()` or `this.foo[bar]()` (x2)
// this also matches subtypes of `Function`, like `interface Foo extends Function {}`. (x2)
// (x6)
// For weird TS reasons that I don't understand, these are (x2)
// safe to construct if: (x2)
// - they have at least one call signature _that is not void-returning_, (x2)
// - OR they have at least one construct signature. (x4)
// safe to call (including as template) if: (x2)
// - they have at least one call signature (x2)

Code
function checkCall(
      node: TSESTree.Node,
      reportingNode: TSESTree.Node,
      unsafeMessageId: Extract<MessageIds, `unsafe${string}`>,
      errorMessageId: Extract<MessageIds, `error${string}`>,
    ): void {
      const type = getConstrainedTypeAtLocation(services, node);

      if (isTypeAnyType(type)) {
        if (!isNoImplicitThis) {
          // `this()` or `this.foo()` or `this.foo[bar]()`
          const thisExpression = getThisExpression(node);
          if (
            thisExpression &&
            isTypeAnyType(
              getConstrainedTypeAtLocation(services, thisExpression),
            )
          ) {
            unsafeMessageId = 'unsafeCallThis';
            errorMessageId = 'errorCallThis';
          }
        }

        const isErrorType = tsutils.isIntrinsicErrorType(type);

        context.report({
          node: reportingNode,
          messageId: isErrorType ? errorMessageId : unsafeMessageId,
          data: {
            type: 'an `any`',
          },
        });
        return;
      }

      if (isBuiltinSymbolLike(services.program, type, 'Function')) {
        // this also matches subtypes of `Function`, like `interface Foo extends Function {}`.
        //
        // For weird TS reasons that I don't understand, these are
        //
        // safe to construct if:
        // - they have at least one call signature _that is not void-returning_,
        // - OR they have at least one construct signature.
        //
        // safe to call (including as template) if:
        // - they have at least one call signature
        // - OR they have at least one construct signature.

        const constructSignatures = type.getConstructSignatures();
        if (constructSignatures.length > 0) {
          return;
        }

        const callSignatures = type.getCallSignatures();
        if (unsafeMessageId === 'unsafeNew') {
          if (
            callSignatures.some(
              signature =>
                !tsutils.isIntrinsicVoidType(signature.getReturnType()),
            )
          ) {
            return;
          }
        } else if (callSignatures.length > 0) {
          return;
        }

        context.report({
          node: reportingNode,
          messageId: unsafeMessageId,
          data: {
            type: 'a `Function`',
          },
        });
        return;
      }
    }

Type Aliases

MessageIds

type MessageIds = | 'errorCall'
  | 'errorCallThis'
  | 'errorNew'
  | 'errorTemplateTag'
  | 'unsafeCall'
  | 'unsafeCallThis'
  | 'unsafeNew'
  | 'unsafeTemplateTag';

Generated by Syntax Scribe