Skip to content

Bug: [no-base-to-string] False negative for unknown type #10862

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

Open
4 tasks done
OliverJAsh opened this issue Feb 21, 2025 · 11 comments
Open
4 tasks done

Bug: [no-base-to-string] False negative for unknown type #10862

OliverJAsh opened this issue Feb 21, 2025 · 11 comments
Labels
accepting prs Go ahead, send a pull request that resolves this issue bug Something isn't working package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin

Comments

@OliverJAsh
Copy link
Contributor

Before You File a Bug Report Please Confirm You Have Done The Following...

  • I have tried restarting my IDE and the issue persists.
  • I have updated to the latest version of the packages.
  • I have searched for related issues and found none that matched my issue.
  • I have read the FAQ and my problem is not listed.

Playground Link

https://typescript-eslint.io/play/#ts=5.7.2&fileType=.tsx&code=PTAEFEA8AcFMGMAusAmpYCcMHsMBpQBzbRUbAO1gDpRBQcgCgBlRDAS3MIAoBvAXwEoA3PXooEAGwCGGWKHgUAzqUgAuUAFdyAa3LYA7uWEgIMBMjSYc%2BIiVC7KNQDLkTFuy6Qh9IA&eslintrc=N4KABGBEBOCuA2BTAzpAXGUEKQAIBcBPABxQGNoBLY-AWhXkoDt8B6Jge1oCMBDZRLXxdk%2BKkwDm6MACZwYAL4gFQA&tsconfig=N4KABGBEDGD2C2AHAlgGwKYCcDyiAuysAdgM6QBcYoEEkJemy0eAcgK6qoDCAFutAGsylBm3TgwAXxCSgA&tokens=false

Repro Code

// Expected error, got one. ✅
String({});

declare const x: unknown;
// Expected error, got none. ❌
String(x);

ESLint Config

module.exports = {
  parser: "@typescript-eslint/parser",
  rules: {
    "@typescript-eslint/no-base-to-string": 2,
  },
};

tsconfig

Expected Result

Expected error.

Actual Result

No error.

Additional Info

No response

@OliverJAsh OliverJAsh added bug Something isn't working package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin triage Waiting for team members to take a look labels Feb 21, 2025
@kirkwaiblinger
Copy link
Member

kirkwaiblinger commented Feb 21, 2025

+1

Note this came up in discussion before here: #10005 (comment)

and restrict-template-expressions does flag unknown-valued expressions

@typescript-eslint/triage-team

@bradzacher
Copy link
Member

bradzacher commented Feb 22, 2025

This is actually working by design - the rule purposely ignores any/unknown.

https://github.com/typescript-eslint/typescript-eslint/blob/main/packages%2Feslint-plugin%2Fsrc%2Frules%2Fno-base-to-string.ts#L254-L257

The reason we ignore it is because we cannot reason about the types. Generally we ignore any/unknown in our rules.

I can see that you added tests for this case in #10432 Kirk heh.

I'm personally in favour of always ignoring any/unknown and instead always and relegating the handling of those types to the no-unsafe-* rules.

@kirkwaiblinger
Copy link
Member

Hmm, that makes sense for any but wouldn't address unknown though

@Josh-Cena
Copy link
Member

String(unknown) is very useful in a generic logging context and we can't really reason about its behavior otherwise. What's a good alternative?

@kirkwaiblinger
Copy link
Member

kirkwaiblinger commented Feb 23, 2025

String(unknown) is very useful in a generic logging context and we can't really reason about its behavior otherwise. What's a good alternative?

Well, that was basically my question here: #10005 (comment). My understanding of that conversation was that JSON.stringify(x) is what you recommend...?

String(x) works reasonably well for all primitives but not most objects. JSON.stringify(x) works arguably better for some primtiives, but not at all for others, works well for POJOs, but works badly for other objects, regardless of .toString().

Why is String(x) prohibited for null | undefined | boolean | string | number | bigint | symbol | Function | object (due to the inclusion of object) but allowed for unknown? I do think that String(unknown) is quite useful but it feels like it's inconsistent for it to be allowed granted the rest of the rule's behavior.

@kirkwaiblinger
Copy link
Member

I can see that you added tests for this case in #10432 Kirk heh.

Lol that is pretty funny 😀. Granted the context, I'm pretty sure that that was more of a descriptive snapshot of relevant codepaths that I wanted to be sure my changes didn't change, rather than a normative belief on my part 😁

@OliverJAsh
Copy link
Contributor Author

For logging functions I like to use a Json type so I can check that the provided value is definitely serializable:

export type Json = boolean | number | string | null | JsonArray | JsonRecord
export interface JsonRecord {
  readonly [key: string]: Json
}
export interface JsonArray extends ReadonlyArray<Json> {}

export const log = (value: Json) => {
  // …
};

@bradzacher
Copy link
Member

log({ toJSON() { throw "Oopsies"; } });

Isn't JavaScript just the best language?

@OliverJAsh
Copy link
Contributor Author

Haha, yeah, I know it's not bulletproof, but I think it strikes a good balance.

@JoshuaKGoldberg
Copy link
Member

String(any) is also caught by no-unsafe-argument. +1 from me on having this rule ignore any.

As for unknown, I could see why someone would want to flag it - and also why others would want to allow it for logging/other purposes. Proposal: how about an opt-in option for flagging unknown?

@JoshuaKGoldberg JoshuaKGoldberg added accepting prs Go ahead, send a pull request that resolves this issue and removed triage Waiting for team members to take a look labels Apr 7, 2025
@JoshuaKGoldberg
Copy link
Member

Accepting PRs for an opt-in option with a name like checkUnknown.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepting prs Go ahead, send a pull request that resolves this issue bug Something isn't working package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin
Projects
None yet
Development

No branches or pull requests

5 participants