1"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([["5219"],{73735(e,n,t){t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>c,default:()=>u,frontMatter:()=>l,metadata:()=>s,toc:()=>p});var s=t(65470),i=t(65723),r=t(7143),o=t(70503),a=t(46742);let l={authors:"joshuakgoldberg",description:"How typescript-eslint expands on TypeScript's type safety to catch explicit and implicit `any`s.",slug:"avoiding-anys",tags:["any","no-explicit-any","no-unsafe","noImplicitAny","typed linting"],title:"Avoiding `any`s with Linting and TypeScript"},c,d={authorsImageUrls:[void 0]},p=[{value:"<code>noImplicitAny</code> Isn't Enough",id:"noimplicitany-isnt-enough",level:2},{value:"Banning Unsafe Types",id:"banning-unsafe-types",level:2},{value:"Banning Explicit <code>any</code>s",id:"banning-explicit-anys",level:3},{value:"Prefer <code>unknown</code>",id:"prefer-unknown",level:4},{value:"Banning <code>Function</code>",id:"banning-function",level:3},{value:"Enforcing <code>unknown</code> for Caught Exceptions",id:"enforcing-unknown-for-caught-exceptions",level:3},{value:"Banning Usage of Unsafe Types",id:"banning-usage-of-unsafe-types",level:2},{value:"Additional Helpers",id:"additional-helpers",level:2},{value:"Disabling TypeScript Suppressions",id:"disabling-typescript-suppressions",level:3},{value:"ESLint Comments Plugin",id:"eslint-comments-plugin",level:3},{value:"<code>ts-reset</code>
1",id:"ts-reset",level:3},{value:"Next Steps",id:"next-steps",level:2}];function h(e){let n={a:"a",admonition:"admonition",code:"code",h2:"h2",h3:"h3",h4:"h4",li:"li",ol:"ol",p:"p",pre:"pre",section:"section",sup:"sup",ul:"ul",...(0,r.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)(n.p,{children:["TypeScript's ",(0,i.jsx)(n.code,{children:"any"})," type is, by design, the single most unsafe part of its type system.\nThe ",(0,i.jsx)(n.code,{children:"any"})," type indicates a type that can be anything and can be used in place of any other type.\nUsing ",(0,i.jsx)(n.code,{children:"any"})," is unsafe because the type disables many of TypeScript's type-checking features and hampers TypeScript's ability to provide developer assistance."]}),"\n",(0,i.jsxs)(n.p,{children:["typescript-eslint includes several lint rules that help prevent unsafe practices around the ",(0,i.jsx)(n.code,{children:"any"})," type.\nThese rules flag uses of ",(0,i.jsx)(n.code,{children:"any"})," or code patterns that sneakily introduce it."]}),"\n",(0,i.jsxs)(n.p,{children:["In this blog post, we'll show you those lint rules and several other handy ways to prevent ",(0,i.jsx)(n.code,{children:"any"}),"s from sneaking into your code."]}),"\n","\n",(0,i.jsxs)(n.h2,{id:"noimplicitany-isnt-enough",children:[(0,i.jsx)(n.code,{children:"noImplicitAny"})," Isn't Enough"]}),"\n",(0,i.jsxs)(n.p,{children:["TypeScript includes a ",(0,i.jsx)(n.a,{href:"https://www.typescriptlang.org/tsconfig/#noImplicitAny",children:(0,i.jsx)(n.code,{children:"noImplicitAny"})})," flag to report when a better type than ",(0,i.jsx)(n.code,{children:"any"})," can't be inferred for a value.\n",(0,i.jsx)(n.code,{children:"noImplicitAny"})," is part of its family of ",(0,i.jsxs)(n.a,{href:"https://www.typescriptlang.org/tsconfig/#strict",children:[(0,i.jsx)(n.code,{children:"strict"})," compiler options"]})," and is generally recommended for all projects."]}),"\n",(0,i.jsxs)(n.p,{children:["However, even with ",(0,i.jsx)(n.code,{children:"noImplicitAny"})," enabled, the ",(0,i.jsx)(n.code,{children:"any"})," type can easily be introduced into codebases.\n",(0,i.jsx)(n.code,{children:"noImplicitAny"})," doesn't prevent developers from explicitly writing the ",(0,i.jsx)(n.code,{children:"any"})," type in type type annotations.\nSome built-in APIs such as ",(0,i.jsx)(n.code,{children:"JSON.stringify"})," and types such as ",(0,i.jsx)(n.code,{children:"Function"})," can introduce the ",(0,i.jsx)(n.code,{children:"any"})," type without triggering ",(0,i.jsx)(n.code,{children:"noImplicitAny"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["Enabling ",(0,i.jsx)(n.code,{children:"noImplicitAny"})," is a great first step towards better project type safety, but it's not enough to prevent ",(0,i.jsx)(n.code,{children:"any"}),"s altogether."]}),"\n",(0,i.jsx)(n.h2,{id:"banning-unsafe-types",children:"Banning Unsafe Types"}),"\n",(0,i.jsxs)(n.p,{children:["The first line of defense against ",(0,i.jsx)(n.code,{children:"any"})," for many repositories is adding lint rules that report on explicitly written unsafe types.\nDevelopers can always ",(0,i.jsx)(n.a,{href:"https://eslint.org/docs/latest/use/configure/rules#use-configuration-comments",children:"disable ESLint rules with inline comments"}),", so this isn't guaranteed to prevent explicit ",(0,i.jsx)(n.code,{children:"any"}),"s from popping up.\nBut these lint rules put an immediate restriction on unsafe types -- and help guide towards better alternatives."]}),"\n",(0,i.jsxs)(n.h3,{id:"banning-explicit-anys",children:["Banning Explicit ",(0,i.jsx)(n.code,{children:"any"}),"s"]}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"/rules/no-explicit-any",children:(0,i.jsx)(n.code,{children:"@typescript-eslint/no-explicit-any"})})," reports on any instance of the ",(0,i.jsx)(n.code,{children:"any"})," type written in your source code.\nDoing so helps prevent developers from using ",(0,i.jsx)(n.code,{children:"any"})," instead of a more safe type."]}),"\n",(0,i.jsxs)(n.p,{children:["Take the following unsafe declaration of ",(0,i.jsx)(n.code,{children:"friend: any"})," in a ",(0,i.jsx)(n.code,{children:"greet"})," function.\nBecause its ",(0,i.jsx)(n.code,{children:"friend"})," parameter is typed as ",(0,i.jsx)(n.code,{children:"any"})," instead of ",(0,i.jsx)(n.code,{children:"string"}),", TypeScript won't report a type error on a call that provides another type.\n",(0,i.jsx)(n.code,{children:"@typescript-eslint/no-explicit-any"})," would report on that ",(0,i.jsx)(n.code,{children:"any"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-ts",children:"function greet(friend: any) {\n // ~~~\n // eslint(@typescript-eslint/no-explicit-any):\n // Unexpected any. Specify a different type.\n console.log(`Hello, ${friend.toUpperCase()}!`);\n}\n\ngreet('Lazlo'); // Ok\n\ngreet({ name: 'Nadya' }); // Should be a type error, but isn't\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Instead of ",(0,i.jsx)(n.code,{children:"any"}),", it would have been more type safe to give ",(0,i.jsx)(n.code,{children:"friend"})," the more precise type ",(0,i.jsx)(n.code,{children:"string"}),"."]}),"\n",(0,i.jsxs)(n.h4,{id:"prefer-unknown",children:["Prefer ",(0,i.jsx)(n.code,{children:"unknown"})]}),"\n",(0,i.jsxs)(n.p,{children:["If you have data that is of an unknown type, instead of describing it with the unsafe ",(0,i.jsx)(n.code,{children:"any"})," type, prefer the safer ",(0,i.jsx)(n.code,{children:"unknown"})," instead.\n",(0,i.jsx)(n.code,{children:"unknown"})," is almost always preferable because it doesn't allow using the value in any arbitrary, potentially unsafe way."]}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"/rules/no-explicit-any",children:(0,i.jsx)(n.code,{children:"@typescript-eslint/no-explicit-any"})}),"'s rule reports include a suggestion fixer that switches the explicit ",(0,i.jsx)(n.code,{children:"any"})," to ",(0,i.jsx)(n.code,{children:"unknown"}),"."]}),"\n",(0,i.jsxs)(n.h3,{id:"banning-function",children:["Banning ",(0,i.jsx)(n.code,{children:"Function"})]}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"/rules/no-unsafe-function-type",children:(0,i.jsx)(n.code,{children:"@typescript-eslint/no-unsafe-function-type"})})," reports on any instance of the built-in ",(0,i.jsx)(n.code,{children:"Function"})," type written in source code.\n",(0,i.jsx)(n.code,{children:"Function"})," is a loose, unsafe type: it allows being called with any number of arguments and returns type ",(0,i.jsx)(n.code,{children:"any"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["Take the following version of ",(0,i.jsx)(n.code,{children:"greet"})," that takes in a function for its parameter.\n",(0,i.jsx)(n.code,{children:"Function"})," doesn't describe any parameter or return types, so TypeScript can't know
1what types it's meant to take in or return.\n",(0,i.jsx)(n.code,{children:"@typescript-eslint/no-unsafe-function-type"})," would report on that ",(0,i.jsx)(n.code,{children:"Function"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-ts",children:"function greet(getFriend: Function) {\n // ~~~~~~~~\n // eslint(@typescript-eslint/no-unsafe-function-type):\n // The `Function` type accepts any function-like value.\n // Prefer explicitly defining any function parameters and return type.\n console.log(`Hello, ${getFriend().toUpperCase()}!`);\n}\n\ngreet(() => 'Lazlo'); // Ok\n\ngreet(() => ({ name: 'Nadya' })); // Should be a type error, but isn't\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Instead of ",(0,i.jsx)(n.code,{children:"Function"}),", it would have been more type safe to give ",(0,i.jsx)(n.code,{children:"getFriend"})," the more precise type ",(0,i.jsx)(n.code,{children:"() => string"}),"."]}),"\n",(0,i.jsxs)(n.h3,{id:"enforcing-unknown-for-caught-exceptions",children:["Enforcing ",(0,i.jsx)(n.code,{children:"unknown"})," for Caught Exceptions"]}),"\n",(0,i.jsxs)(n.p,{children:["There is no way to indicate what types functions may throw in TypeScript, due to the highly dynamic nature of JavaScript",(0,i.jsx)(n.sup,{children:(0,i.jsx)(n.a,{href:"#user-content-fn-no-throws-204727",id:"user-content-fnref-no-throws-204727","data-footnote-ref":!0,"aria-describedby":"footnote-label",children:"1"})}),".\nTherefore, TypeScript treats all caught values as ",(0,i.jsx)(n.code,{children:"any"})," by default.\nTypeScript's ",(0,i.jsx)(n.a,{href:"https://www.typescriptlang.org/tsconfig/#useUnknownInCatchVariables",children:(0,i.jsx)(n.code,{children:"useUnknownInCatchVariables"})})," changes ",(0,i.jsx)(n.code,{children:"catch"})," block variables to have the more appropriate ",(0,i.jsx)(n.code,{children:"unknown"})," type, but no compiler option equivalent exists for ",(0,i.jsx)(n.code,{children:"Promise"})," rejections' ",(0,i.jsx)(n.code,{children:".catch()"})," method."]}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"/rules/use-unknown-in-catch-callback-variable",children:(0,i.jsx)(n.code,{children:"@typescript-eslint/use-unknown-in-catch-callback-variable"})})," enforces always using the ",(0,i.jsx)(n.code,{children:"unknown"})," type for the parameter of a Promise rejection callback."]}),"\n",(0,i.jsx)(n.p,{children:"For example, given the following code, TypeScript would not report a type error, but the lint rule would report:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-ts",children:"function rejectWith(value: string) {\n return Promise.reject(value);\n}\n\nrejectWith('Nandor').catch(error => {\n // ~~~~~\n // eslint(@typescript-eslint/use-unknown-in-catch-callback-variable):\n // Prefer the safe `: unknown` for a `catch` callback variable.\n\n console.log(error.message); // Should be a type error, but isn't\n});\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Had the ",(0,i.jsx)(n.code,{children:"error"})," been given a ",(0,i.jsx)(n.code,{children:": unknown"})," type, TypeScript would be able to report on ",(0,i.jsx)(n.code,{children:"error.message"})," as being unsafe."]}),"\n",(0,i.jsx)(n.h2,{id:"banning-usage-of-unsafe-types",children:"Banning Usage of Unsafe Types"}),"\n",(0,i.jsxs)(n.p,{children:["Once an ",(0,i.jsx)(n.code,{children:"any"}),' type exists in code, it is "infectious": it can turn the types of values based on it into more ',(0,i.jsx)(n.code,{children:"any"}),"s."]}),"\n",(0,i.jsxs)(n.p,{children:["typescript-eslint includes a collection of rules that flag code patterns which make use of ",(0,i.jsx)(n.code,{children:"any"}),"'s unsafe, viral nature.\nEach of the following rules reports on a specific use of ",(0,i.jsx)(n.code,{children:"any"}),"."]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/rules/no-unsafe-argument",children:(0,i.jsx)(n.code,{children:"@typescript-eslint/no-unsafe-argument"})})," reports on passing an ",(0,i.jsx)(n.code,{children:"any"})," typed value to a function call"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/rules/no-unsafe-assignment",children:(0,i.jsx)(n.code,{children:"@typescript-eslint/no-unsafe-assignment"})})," reports on assigning an ",(0,i.jsx)(n.code,{children:"any"})," typed value to a property or variable"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/rules/no-unsafe-call",children:(0,i.jsx)(n.code,{children:"@typescript-eslint/no-unsafe-call"})})," reports on calling an ",(0,i.jsx)(n.code,{children:"any"})," typed value like a function"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/rules/no-unsafe-member-access",children:(0,i.jsx)(n.code,{children:"@typescript-eslint/no-unsafe-member-access"})})," reports on accessing members of any value typed as ",(0,i.jsx)(n.code,{children:"any"})]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/rules/no-unsafe-return",children:(0,i.jsx)(n.code,{children:"@typescript-eslint/no-unsafe-return"})})," reports on returning a value typed as ",(0,i.jsx)(n.code,{children:"any"})," from a function"]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["For example, this ",(0,i.jsx)(n.code,{children:"parseData"})," function violates several of those lint rules by creating a ",(0,i.jsx)(n.code,{children:"shape"})," value of type ",(0,i.jsx)(n.code,{children:"any"}
1)," and not validating its type before using it:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-ts",children:"export interface Shape {\n label: string;\n value: number;\n}\n\nexport function parseShapeFromData(raw: string): Shape {\n const shape = JSON.parse(raw);\n // ~~~~~~~~~~~~~~~~~~~~~~~\n // eslint(@typescript-eslint/no-unsafe-assignment):\n // Unsafe assignment of an `any` value.\n\n console.log('Making a shape with value:', shape.value);\n // ~~~~~\n // eslint(@typescript-eslint/no-unsafe-member-access):\n // Unsafe member access .value of an `any` value.\n\n return shape;\n // eslint(@typescript-eslint/no-unsafe-return):\n // Unsafe return of an `any` value.\n}\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Had the ",(0,i.jsx)(n.code,{children:"parseShapeFromData"})," function included checks on the type of the ",(0,i.jsx)(n.code,{children:"shape"})," value or used a schema validation library such as ",(0,i.jsx)(n.a,{href:"https://zod.dev",children:"Zod"}),", it would be informed at runtime if its ",(0,i.jsx)(n.code,{children:"raw"})," string didn't create the expected ",(0,i.jsx)(n.code,{children:"Shape"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["Put together, the ",(0,i.jsx)(n.code,{children:"@typescript-eslint/no-unsafe-*"})," rules around ",(0,i.jsx)(n.code,{children:"any"})," safety provide a comprehensive suite of checks that can flag most accidental uses of ",(0,i.jsx)(n.code,{children:"any"})," across a codebase.\nWe highly recommend using them alongside TypeScript's ",(0,i.jsx)(n.code,{children:"noImplicitAny"})," compiler option."]}),"\n",(0,i.jsx)(n.h2,{id:"additional-helpers",children:"Additional Helpers"}),"\n",(0,i.jsx)(n.h3,{id:"disabling-typescript-suppressions",children:"Disabling TypeScript Suppressions"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"any"})," type is not the only way to bypass TypeScript's type system.\nDevelopers are also able to use inline TypeScript directives: ",(0,i.jsx)(n.code,{children:"// @ts-expect-error"})," and ",(0,i.jsx)(n.code,{children:"// @ts-ignore"}),".\nThose inline comment directives cause TypeScript to ignore type errors on a line.\nDisabling TypeScript on a per-line basis can sometimes be necessary in rare cases, but is always unsafe and should not be part of your standard toolkit."]}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"// @ts-expect-error"})," and ",(0,i.jsx)(n.code,{children:"// @ts-ignore"})," are almost the same, except ",(0,i.jsx)(n.code,{children:"// @ts-expect-error"})," will produce a new error if there isn't any existing error for it to suppress.\nIt's generally preferable to use ",(0,i.jsx)(n.code,{children:"// @ts-expect-error"})," over ",(0,i.jsx)(n.code,{children:"// @ts-ignore"}),"."]})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"/rules/ban-ts-comment",children:(0,i.jsx)(n.code,{children:"@typescript-eslint/ban-comment"})})," can be used to report on cases where developers use TypeScript comment directives.\nBy default, the rule:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Prohibits all ",(0,i.jsx)(n.code,{children:"@ts-ignore"})," and ",(0,i.jsx)(n.code,{children:"@ts-nocheck"})," comment directives"]}),"\n",(0,i.jsxs)(n.li,{children:["Prohibits ",(0,i.jsx)(n.code,{children:"@ts-expect-error"})," directives unless they have an explanatory comment description"]}),"\n",(0,i.jsx)(n.li,{children:"Requires explanatory comment descriptions to contain at least 3 characters"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["For example, suppose ",(0,i.jsx)(n.code,{children:'"@example/package"'})," exports a ",(0,i.jsx)(n.code,{children:"processString"})," function that should take in a ",(0,i.jsx)(n.code,{children:"string"})," but whose types incorrectly indicate take in a ",(0,i.jsx)(n.code,{children:"number"}),".\nA ",(0,i.jsx)(n.code,{children:"// @ts-expect-error"})," comment could be used to tell TypeScript to ignore the line:"]}),"\n",(0,i.jsxs)(o.A,{children:[(0,i.jsx)(a.A,{value:"\u274C Incorrect",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-ts",children:"import { processString } from '@example/package';\n\n// @ts-ignore\nprocessString('New York City');\n"})})}),(0,i.jsx)(a.A,{value:"\u2705 Correct",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-ts",children:"import { processString } from '@example/package';\n\n// @ts-expect-error -- Pending updating the processString types. See GH-1234.\nprocessString('New York City');\n"})})})]}),"\n",(0,i.jsxs)(n.p,{children:["Once the types for ",(0,i.jsx)(n.code,{children:"@example/package"})," are fixed to no longer produce a type error, the comment directive will itself produce a type error asking to delete itself."]}),"\n",(0,i.jsx)(n.h3,{id:"eslint-comments-plugin",children:"ESLint Comments Plugin"}),"\n",(0,i.jsxs)(n.p,{children:["ESLint includes its own inline comment directives separate from TypeScript's.\n",(0,i.jsx)(n.code,{children:"// eslint-disable"}),", ",(0,i.jsx)(n.code,{children:"// eslint-disable-next-line"}),", and other inline ESLint config comments can suppress lint rules \u2014 including those mentioned in this post.\nSuppressing ESLint rules can also be necessary in cases where the rule and/or TypeScript's type system have misunderstand your code."]}),"\n",(0,i.jsxs)(n.p,{children:["If you must use ESLint comment directives, we recommend utilizing ",(0,i.jsx)(n.a,{href:"https://eslint-community.github.io/eslint-plugin-eslint-comments",children:(0,i.jsx)(n.code,{children:"@eslint-community/eslint-plugin-eslint-comments"})}),".\nThe plugin's ",(0,i.jsx)(n.a,{href:"https://eslint-community.github.io/eslint-plugin-eslint-comments/#%F0%9F%93%96-usage",children:"recommended preset"})," enables rules that enforce best practices around ESLint comment directives, including:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"https://eslint-community.github.io/eslint-plugin-eslint-comments/rules/disable-enable-pair.html",children:(0,i.jsx)(n.code,{children:"@eslint-community/eslint-comments/disable-enable-pair"})}),": to prevent accidentally disabling rules for the rest of a file, rather than on specific lines"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"https://eslint-community.github.io/eslint-plugin-eslint-comments/rules/no-unlimited-disable.html",children:(0,i.jsx)(n.code,{children:"@eslint-community/eslint-comments/no-unlimited-disable"})}),": to prevent accidentally disabling all ESLint rules, rather than specific ones"]}
1),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"https://eslint-community.github.io/eslint-plugin-eslint-comments/rules/require-description.html",children:(0,i.jsx)(n.code,{children:"@eslint-community/eslint-comments/require-description"})}),": to require comments explaining why directives are necessary"]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["For example, suppose the type of an imported variable is temporarily ",(0,i.jsx)(n.code,{children:"any"})," pending some soon-to-be-completed refactoring.\nIt might be reasonable to include an ESLint disable comment to suppress a lint rule reporting on the ",(0,i.jsx)(n.code,{children:"any"}),".\nThat comment should ideally include an explanation and link to the pending task, so developers know it's not normal to disable lint rules without good reason:"]}),"\n",(0,i.jsxs)(o.A,{children:[(0,i.jsx)(a.A,{value:"\u274C Incorrect",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-ts",children:"import { processString } from '@example/package';\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-call\nprocessString('New York City');\n"})})}),(0,i.jsx)(a.A,{value:"\u2705 Correct",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-ts",children:"import { processString } from '@example/package';\n\n// Pending GH-1234: will soon not be any.\n// eslint-disable-next-line @typescript-eslint/no-unsafe-call\nprocessString('New York City');\n"})})})]}),"\n",(0,i.jsxs)(n.p,{children:["Once the types for ",(0,i.jsx)(n.code,{children:"@example/package"})," are fixed to no longer produce an ",(0,i.jsx)(n.code,{children:"any"}),", running ESLint with ",(0,i.jsx)(n.a,{href:"https://eslint.org/docs/latest/use/configure/rules#report-unused-eslint-disable-comments",children:(0,i.jsx)(n.code,{children:"reportUnusedDisableDirectives"})})," option will produce a lint report asking to remove the comment directive."]}),"\n",(0,i.jsx)(n.h3,{id:"ts-reset",children:(0,i.jsx)(n.code,{children:"ts-reset"})}),"\n",(0,i.jsxs)(n.p,{children:["Some sources of ",(0,i.jsx)(n.code,{children:"any"})," come from built-in global types provided by TypeScript.\n",(0,i.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse",children:(0,i.jsx)(n.code,{children:"JSON.parse()"})}),", ",(0,i.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Response/json",children:(0,i.jsx)(n.code,{children:".json()"})}),", and ",(0,i.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#basic_concepts",children:(0,i.jsx)(n.code,{children:"Storage"})})," properties are all typed as ",(0,i.jsx)(n.code,{children:"any"})," by default in TypeScript.\nTypeScript keeps ",(0,i.jsx)(n.code,{children:"any"})," in the types for legacy support reasons",(0,i.jsx)(n.sup,{children:(0,i.jsx)(n.a,{href:"#user-content-fn-compiler-option-lib-any-204727",id:"user-content-fnref-compiler-option-lib-any-204727","data-footnote-ref":!0,"aria-describedby":"footnote-label",children:"2"})}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.a,{href:"https://www.totaltypescript.com/ts-reset",children:(0,i.jsx)(n.code,{children:"ts-reset"})})," library switches those global type definitions to safer equivalents.\nIt switches the ",(0,i.jsx)(n.code,{children:"any"}),"s in those built-in types to ",(0,i.jsx)(n.code,{children:"unknown"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["With ",(0,i.jsx)(n.code,{children:"ts-reset"}),", the following ",(0,i.jsx)(n.code,{children:"data"})," variable would switch from being type ",(0,i.jsx)(n.code,{children:"any"})," to ",(0,i.jsx)(n.code,{children:"unknown"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-ts",children:'const data = JSON.parse(`"clearly-a-string"`);\n// ^? any (without ts-reset)\n// ^? unknown (with ts-reset)\n\nconsole.log(data.some.property.that.does.not.exist);\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Note that ",(0,i.jsx)(n.code,{children:"ts-reset"})," applies globally, so it should only be used in application code."]}),"\n",(0,i.jsx)(n.h2,{id:"next-steps",children:"Next Steps"}),"\n",(0,i.jsxs)(n.p,{children:["We highly recommend using at least the ",(0,i.jsx)(n.a,{href:"/users/configs#recommended-type-checked",children:(0,i.jsx)(n.code,{children:"tseslint.configs.recommendedTypeChecked"})})," preset in your ESLint configuration.\nIt enables the ",(0,i.jsx)(n.code,{children:"no-explicit-any"})," and ",(0,i.jsx)(n.code,{children:"no-unsafe-*"})," lint rules mentioned in this blog post, as well as a large set of other rules that help enforce type safety and TypeScript best practices."]}),"\n",(0,i.jsxs)(n.p,{children:["If you're interested in achieving stronger type safety with more strict linting, consider upgrading to the ",(0,i.jsx)(n.a,{href:"/users/configs#strict-type-checked",children:(0,i.jsx)(n.code,{children:"tseslint.configs.strictTypeChecked"})})," preset.\nIt includes all the recommended rules, as well as ",(0,i.jsx)(n.code,{children:"use-unknown-in-catch-callback-variable"})," and other rules that enforce more strict type safety and TypeScript best practices."]}),"\n","\n",(0,i.jsxs)(n.section,{"data-footnotes":!0,className:"footnotes",children:[(0,i.jsx)(n.h2,{className:"sr-only",id:"footnote-label",children:"Footnotes"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{id:"user-content-fn-no-throws-204727",children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsxs)(n.a,{href:"https://www.learningtypescript.com/articles/why-typescript-doesnt-include-
1a-throws-keyword",children:["Why TypeScript Doesn't Include a ",(0,i.jsx)(n.code,{children:"throws"})," Keyword"]})," ",(0,i.jsx)(n.a,{href:"#user-content-fnref-no-throws-204727","data-footnote-backref":"","aria-label":"Back to reference 1",className:"data-footnote-backref",children:"\u21A9"})]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{id:"user-content-fn-compiler-option-lib-any-204727",children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"https://github.com/microsoft/TypeScript/issues/60899",children:"microsoft/TypeScript#60899 Compiler option to switch lib .d.ts anys to unknown"})," ",(0,i.jsx)(n.a,{href:"#user-content-fnref-compiler-option-lib-any-204727","data-footnote-backref":"","aria-label":"Back to reference 2",className:"data-footnote-backref",children:"\u21A9"})]}),"\n"]}),"\n"]}),"\n"]})]})}function u(e={}){let{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},46742(e,n,t){t.d(n,{A:()=>a});var s=t(65723);t(22155);var i=t(70851),r=t(28226);function o({children:e,className:n,hidden:t}){return(0,s.jsx)("div",{role:"tabpanel",className:(0,i.A)("tabItem_eQ9s",n),hidden:t,children:e})}function a({children:e,className:n,value:t}){let{selectedValue:i,lazy:l}=(0,r.uc)(),c=t===i;return!c&&l?null:(0,s.jsx)(o,{className:n,hidden:!c,children:e})}},70503(e,n,t){t.d(n,{A:()=>h});var s=t(65723);t(22155);var i=t(70851),r=t(98838),o=t(28226),a=t(26309),l=t(29021);function c({className:e}){let{selectedValue:n,selectValue:t,tabValues:r,block:l}=(0,o.uc)(),d=[],{blockElementScrollPositionUntilNextRender:p}=(0,a.a_)(),h=e=>{let s=e.currentTarget,i=r[d.indexOf(s)].value;i!==n&&(p(s),t(i))},u=e=>{let n=null;switch(e.key){case"Enter":h(e);break;case"ArrowRight":{let t=d.indexOf(e.currentTarget)+1;n=d[t]??d[0];break}case"ArrowLeft":{let t=d.indexOf(e.currentTarget)-1;n=d[t]??d[d.length-1]}}n?.focus()};return(0,s.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,i.A)("tabs",{"tabs--block":l},e),children:r.map(({value:e,label:t,attributes:r})=>(0,s.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{d.push(e)},onKeyDown:u,onClick:h,...r,className:(0,i.A)("tabs__item","tabItem_e1w9",r?.className,{"tabs__item--active":n===e}),children:t??e},e))})}function d({children:e}){return(0,s.jsx)("div",{className:"margin-top--md",children:e})}function p({className:e,children:n}){return(0,s.jsxs)("div",{className:(0,i.A)(r.G.tabs.container,"tabs-container","tabList_mVW_"),children:[(0,s.jsx)(c,{className:e}),(0,s.jsx)(d,{children:n})]})}function h(e){let n=(0,l.A)(),t=(0,o.OC)(e);return(0,s.jsx)(o.O_,{value:t,children:(0,s.jsx)(p,{className:e.className,children:(0,o.vT)(e.children)})},String(n))}},28226(e,n,t){t.d(n,{OC:()=>h,O_:()=>x,uc:()=>y,vT:()=>d});var s=t(65723),i=t(22155),r=t(62934),o=t(24127),a=t(12910),l=t(22399),c=t(6660);function d(e){return i.Children.toArray(e).filter(e=>"\n"!==e)}function p({value:e,tabValues:n}){return n.some(n=>n.value===e)}function h(e){let n,{defaultValue:t,queryString:s=!1,groupId:d}=e,h=function(e){let{values:n,children:t}=e;return(0,i.useMemo)(()=>{let e=n??i.Children.toArray(t).flatMap(e=>{if(!e)return[];if((0,i.isValidElement)(e)&&function(e){let{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return[e];let n="string"==typeof e.type?e.type:e.type.name;throw Error(`Docusaurus error: Bad <Tabs> child <${n}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop. 2If you do not want to pass on a "value" prop to the direct children of <Tabs>, you can also pass an explicit <Tabs values={...}> prop.`)}).map(({props:{value:e,label:n,attributes:t,default:s}})=>({value:e,label:n,attributes:t,default:s})),s=(0,l.XI)(e,(e,n)=>e.value===n.value);if(s.length>0)throw Error(`Docusaurus error: Duplicate values "${s.map(e=>`'${e.value}'`).join(", ")}" found in <Tabs>. Every value needs to be unique.`);return e},[n,t])}(e),[u,y]=(0,i.useState)(()=>(function({defaultValue:e,tabValues:n}){if(0===n.length)throw Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!p({value:e,tabValues:n}))throw Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${n.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}let t=n.find(e=>e.default)??n[0];if(!t)throw Error("Unexpected error: 0 tabValues");return t.value})({defaultValue:t,tabValues:h})),[x,f]=function({queryString:e=!1,groupId:n}){let t=(0,r.W6)(),s=function({queryString:e=!1,groupId:n}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!n)throw Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:e,groupId:n});return[(0,a.aZ)(s),(0,i.useCallback)(e=>{if(!s)return;let n=new URLSearchParams(t.location.search);n.set(s,e),t.replace({...t.location,search:n.toString()})},[s,t])]}({queryString:s,groupId:d}),[m,g]=function({groupId:e}){let n=e?`docusaurus.tab.${e}`:null,[t,s]=(0,c.Dv)(n);return[t,(0,i.useCallback)(e=>{n&&s.set(e)},[n,s])]}({groupId:d}),j=p({value:n=x??m,tabValues:h})?n:null;return(0,o.A)(()=>{j&&y(j)},[j]),{selectedValue:u,selectValue:(0,i.useCallback)(e=>{if(!p({value:e,tabValues:h}))throw Error(`Can't select invalid tab value=${e}`);y(e),f(e),g(e)},[f,g,h]),tabValues:h,lazy:e.lazy??!1,block:e.block??!1}}let u=(0,i.createContext)(null);function y(){let e=i.useContext(u);if(!e)throw Error("useTabsContext() must be used within a Tabs component");return e}function x(e){return(0,s.jsx)(u.Provider,{value:e.value,children:e.children})}},7143(e,n,t){t.d(n,{R:()=>o,x:()=>a});var s=t(22155);let i={},r=s.createContext(i);function o(e){let n=s.useContext(r);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),s.createElement(r.Provider,{value:n},e.children)}},65470(e){e.exports=JSON.parse('{"permalink":"/blog/avoiding-anys","source":"@site/blog/2025-01-21-avoiding-anys.mdx","title":"Avoiding `any`s with Linting and TypeScript","description":"How typescript-eslint expands on TypeScript\'s type safety to catch explicit and implicit `any`s.","date":"2025-01-21T00:00:00.000Z","tags":[{"inline":true,"label":"any","permalink":"/blog/tags/any"},{"inline":true,"label":"no-explicit-any","permalink":"/blog/tags/no-explicit-any"},{"inline":true,"label":"no-unsafe","permalink":"/blog/tags/no-unsafe"},{"inline":true,"label":"noImplicitAny","permalink":"/blog/tags/no-implicit-any"},{"inline":true,"label":"typed linting","permalink":"/blog/tags/typed-linting"}],"readingTime":10.51,"hasTruncateMarker":true,"authors":[{"name":"Josh Goldberg","title":"typescript-eslint Maintainer","url":"https://github.com/JoshuaKGoldberg","imageURL":"/img/team/joshuakgoldberg.jpg","key":"joshuakgoldberg","page":null}],"frontMatter":{"authors":"joshuakgoldberg","description":"How typescript-eslint expands on TypeScript\'s type safety to catch explicit and implicit `any`s.","slug":"avoiding-anys","tags":["any","no-explicit-any","no-unsafe","noImplicitAny","typed linting"],"title":"Avoiding `any`s with Linting and TypeScript"},"unlisted":false,"prevItem":{"title":"Typed Linting with Project Service","permalink":"/blog/project-service"},"nextItem":{"title":"Typed Linting: The Most Powerful TypeScript Linting Ever","permalink":"/blog/typed-linting"}}')}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.