Skip to content

[restrict-template-expressions] errors even when class defines toString() #3538

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
3 tasks done
nicholaschiang opened this issue Jun 16, 2021 · 1 comment
Closed
3 tasks done
Labels
package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin working as intended Issues that are closed as they are working as intended

Comments

@nicholaschiang
Copy link

  • I have tried restarting my IDE and the issue persists.
  • I have updated to the latest version of the packages.
  • I have read the FAQ and my problem is not listed.

Repro

// .eslintrc.js
const path = require('path');

module.exports = {
  root: true,
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint', 'promise'],
  parserOptions: { project: [path.resolve(__dirname, 'tsconfig.json')] },
  extends: [
    'next',
    'airbnb-typescript',
    'airbnb/hooks',
    'plugin:@typescript-eslint/eslint-recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:@typescript-eslint/recommended-requiring-type-checking',
    'plugin:promise/recommended',
    'prettier',
  ],
  env: {
    browser: true,
    node: true,
  },
};
// lib/message.ts
class Message {
  public toString(): string {
    return 'I am a custom toString() method';
  }
}

const message = new Message();
console.log(`This is the message: ${message}`); // results in an error
console.log(`This is the message: ${message.toString()}`); // no error

That ☝️ creates an error saying:

Invalid type "Message" of template literal expression.
// tsconfig.json
{
  "$schema": "http://json.schemastore.org/tsconfig",
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": false,
    "skipLibCheck": true,
    "strict": true,
    "noUnusedLocals": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "downlevelIteration": true,
    "baseUrl": "."
  },
  "exclude": ["node_modules"],
  "include": [
    "next-env.d.ts",
    "types/*.d.ts",
    "lib/**/*.ts",
    "lib/**/*.tsx",
    "components/**/*.ts",
    "components/**/*.tsx",
    "pages/**/*.ts",
    "pages/**/*.tsx",
    "styles/**/*.ts"
  ]
}

Expected Result

There should be no error because the no-base-to-string rule should apply here; ESLint should realize that I've defined a custom toString() method on Message and thus Message is a valid type to be in a template expression.

Actual Result

I got this error from ESLint:

Invalid type "Message" of template literal expression.

Versions

package version
@typescript-eslint/eslint-plugin 4.27.0
@typescript-eslint/parser 4.27.0
TypeScript 4.3.2
ESLint 7.28.0
node 12.8.3
@nicholaschiang nicholaschiang added package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin triage Waiting for team members to take a look labels Jun 16, 2021
@bradzacher
Copy link
Member

This is working as expected.

This rule intentionally does not deal with .toString implementations - it simply enforces that you use a string in the template literal.

As the rule's description says:

Enforce template literal expressions to be of string type

Why does it do this?
Because it's about ensuring your code is doing the "right thing".
Boolean, Number, Regex and Array all include toString implementations - but it doesn't mean you want your template expressions to include these values as is. EG:

  • booleans are generally not something you should be exposing to users - so you'd be better replacing them with strings like "Yes" | "No" or "On" | "Off".
  • numbers generally should be formatted and rounded instead of dumping a random decimal to the user
  • regexes are unlikely to be something users will understand, so there's probably a better string you can use to describe what the regex means.
  • an array's toString is just .map(toString).join(',') so you don't have spaces between items, and you don't have control over the items.

Custom objects are no exception here for this lint rule.

@bradzacher bradzacher added working as intended Issues that are closed as they are working as intended and removed triage Waiting for team members to take a look labels Jun 16, 2021
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 17, 2021
axmmisaka referenced this issue in lf-lang/reactor-ts Jun 3, 2023
@typescript-eslint/restrict-template-expressions
@typescript-eslint/restrict-plus-operands
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin working as intended Issues that are closed as they are working as intended
Projects
None yet
Development

No branches or pull requests

2 participants