Skip to content

⬅️ Back to Table of Contents

📄 restrict-template-expressions

📊 Analysis Summary

Metric Count
🔧 Functions 3
📦 Imports 16
📑 Type Aliases 3

📚 Table of Contents

🛠️ File Location:

📂 packages/eslint-plugin/src/rules/restrict-template-expressions.ts

📤 Default Export

export default createRule<Options, MessageId>({ ... })
Property Value
name 'restrict-template-expressions'
meta.type 'problem'
meta.docs.description 'Enforce template literal expressions to be of string type'
meta.docs.recommended.recommended true
meta.docs.recommended.strict [ { allowAny: false, allowBoolean: false, allowNever: false, allowNullish: false, allowNumber: false, allowRegExp: fa...
meta.docs.requiresTypeChecking true
meta.messages.invalidType 'Invalid type "{{type}}" of template literal expression.'
meta.schema [ { type: 'object', additionalProperties: false, properties: { ...Object.fromEntries( optionTesters.map(({ type, opti...
defaultOptions [ { allow: [{ name: ['Error', 'URL', 'URLSearchParams'], from: 'lib' }], allowAny: true, allowBoolean: true, allowNul...

Entry point: create — documented under Functions.


📦 Imports

Name Source
TSESTree @typescript-eslint/utils
Type typescript
TypeChecker typescript
typeMatchesSomeSpecifier @typescript-eslint/type-utils
typeOrValueSpecifiersSchema @typescript-eslint/type-utils
AST_NODE_TYPES @typescript-eslint/utils
TypeFlags typescript
TypeOrValueSpecifier ../util
createRule ../util
getConstrainedTypeAtLocation ../util
getParserServices ../util
getTypeName ../util
isTypeAnyType ../util
isTypeFlagSet ../util
isTypeNeverType ../util
matchesTypeOrBaseType ../util

Functions

create(context: any, [{ allow, ...options }]: any): { TemplateLiteral(node: TSESTree.TemplateLiteral): void; }

Parameters:

  • context any
  • [{ allow, ...options }] any

Returns: { TemplateLiteral(node: TSESTree.TemplateLiteral): void; }

Calls:

  • getParserServices (from ../util)
  • program.getTypeChecker
  • optionTesters.filter
  • getConstrainedTypeAtLocation (from ../util)
  • recursivelyCheckType
  • context.report
  • checker.typeToString
  • innerType.isUnion
  • innerType.types.every
  • innerType.isIntersection
  • innerType.types.some
  • isTypeFlagSet (from ../util)
  • matchesTypeOrBaseType (from ../util)
  • typeMatchesSomeSpecifier (from @typescript-eslint/type-utils)
  • enabledOptionTesters.some
  • tester

Internal Comments:

// don't check tagged template literals

Code
create(context, [{ allow, ...options }]) {
    const services = getParserServices(context);
    const { program } = services;
    const checker = program.getTypeChecker();
    const enabledOptionTesters = optionTesters.filter(
      ({ option }) => options[option],
    );

    return {
      TemplateLiteral(node: TSESTree.TemplateLiteral): void {
        // don't check tagged template literals
        if (node.parent.type === AST_NODE_TYPES.TaggedTemplateExpression) {
          return;
        }

        for (const expression of node.expressions) {
          const expressionType = getConstrainedTypeAtLocation(
            services,
            expression,
          );

          if (!recursivelyCheckType(expressionType)) {
            context.report({
              node: expression,
              messageId: 'invalidType',
              data: { type: checker.typeToString(expressionType) },
            });
          }
        }
      },
    };

    function recursivelyCheckType(innerType: Type): boolean {
      if (innerType.isUnion()) {
        return innerType.types.every(recursivelyCheckType);
      }

      if (innerType.isIntersection()) {
        return innerType.types.some(recursivelyCheckType);
      }

      return (
        isTypeFlagSet(innerType, TypeFlags.StringLike) ||
        matchesTypeOrBaseType(
          services,
          type => typeMatchesSomeSpecifier(type, allow, program),
          innerType,
        ) ||
        enabledOptionTesters.some(({ tester }) =>
          tester(innerType, checker, recursivelyCheckType),
        )
      );
    }
  }

testTypeFlag(flagsToCheck: TypeFlags): OptionTester

Parameters:

  • flagsToCheck TypeFlags

Returns: OptionTester

Code
(flagsToCheck: TypeFlags): OptionTester =>
  type =>
    isTypeFlagSet(type, flagsToCheck)

Internal helpers

Declared inside another function in this file.

recursivelyCheckType(innerType: Type): boolean

Parameters:

  • innerType Type

Returns: boolean

Calls:

  • innerType.isUnion
  • innerType.types.every
  • innerType.isIntersection
  • innerType.types.some
  • isTypeFlagSet (from ../util)
  • matchesTypeOrBaseType (from ../util)
  • typeMatchesSomeSpecifier (from @typescript-eslint/type-utils)
  • enabledOptionTesters.some
  • tester
Code
function recursivelyCheckType(innerType: Type): boolean {
      if (innerType.isUnion()) {
        return innerType.types.every(recursivelyCheckType);
      }

      if (innerType.isIntersection()) {
        return innerType.types.some(recursivelyCheckType);
      }

      return (
        isTypeFlagSet(innerType, TypeFlags.StringLike) ||
        matchesTypeOrBaseType(
          services,
          type => typeMatchesSomeSpecifier(type, allow, program),
          innerType,
        ) ||
        enabledOptionTesters.some(({ tester }) =>
          tester(innerType, checker, recursivelyCheckType),
        )
      );
    }

Type Aliases

OptionTester

type OptionTester = (
  type: Type,
  checker: TypeChecker,
  recursivelyCheckType: (type: Type) => boolean,
) => boolean;

Options

type Options = [
  {
    allow?: TypeOrValueSpecifier[];
  } & Partial<Record<(typeof optionTesters)[number]['option'], boolean>>,
];

MessageId

type MessageId = 'invalidType';

Generated by Syntax Scribe