Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork2.8k
feat(eslint-plugin): [no-unsafe-call] check calls of Function#10010
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -59,6 +59,34 @@ String.raw`foo`; | ||
</TabItem> | ||
</Tabs> | ||
## The Unsafe `Function` Type | ||
The `Function` type is behaves almost identically to `any` when called, so this rule also disallows calling values of type `Function`. | ||
<Tabs> | ||
<TabItem value="❌ Incorrect"> | ||
```ts | ||
const f: Function = () => {}; | ||
f(); | ||
``` | ||
</TabItem> | ||
</Tabs> | ||
Note that whereas [no-unsafe-function-type](./no-unsafe-function-type.mdx) helps prevent the _creation_ of `Function` types, this rule helps prevent the unsafe _use_ of `Function` types, which may creep into your codebase without explicitly referencing the `Function` type at all. | ||
See, for example, the following code: | ||
```ts | ||
function unsafe(maybeFunction: unknown): string { | ||
if (typeof maybeFunction === 'function') { | ||
// TypeScript allows this, but it's completely unsound. | ||
return maybeFunction('call', 'with', 'any', 'args'); | ||
} | ||
// etc | ||
} | ||
``` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. [Praise] Great example, really clear and concise. | ||
## When Not To Use It | ||
If your codebase has many existing `any`s or areas of unsafe code, it may be difficult to enable this rule. | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -6,6 +6,7 @@ | ||
getConstrainedTypeAtLocation, | ||
getParserServices, | ||
getThisExpression, | ||
isBuiltinSymbolLike, | ||
isTypeAnyType, | ||
} from '../util'; | ||
@@ -25,13 +26,13 @@ | ||
requiresTypeChecking: true, | ||
}, | ||
messages: { | ||
unsafeCall: 'Unsafe call ofa(n) {{type}} typed value.', | ||
unsafeCallThis: [ | ||
'Unsafe call ofa(n) {{type}} typed value. `this` is typed as{{type}}.', | ||
'You can try to fix this by turning on the `noImplicitThis` compiler option, or adding a `this` parameter to the function.', | ||
].join('\n'), | ||
unsafeNew: 'Unsafe construction ofa(n) {{type}} typed value.', | ||
unsafeTemplateTag: 'Unsafeuse of a(n) {{type}} typed template tag.', | ||
}, | ||
schema: [], | ||
}, | ||
@@ -74,6 +75,49 @@ | ||
type: isErrorType ? '`error` type' : '`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; | ||
kirkwaiblinger marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
} | ||
const callSignatures = type.getCallSignatures(); | ||
if (messageId === 'unsafeNew') { | ||
if ( | ||
callSignatures.some( | ||
signature => | ||
!tsutils.isIntrinsicVoidType(signature.getReturnType()), | ||
) | ||
) { | ||
return; | ||
} | ||
} else if (callSignatures.length > 0) { | ||
return; | ||
} | ||
context.report({ | ||
node: reportingNode, | ||
messageId, | ||
data: { | ||
type: '`Function`', | ||
}, | ||
}); | ||
return; | ||
} | ||
} | ||
Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.
Uh oh!
There was an error while loading.Please reload this page.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -55,7 +55,7 @@ export function parseTSConfig(code?: string): TSConfig { | ||
const moduleRegexp = /(module\.exports\s*=)/g; | ||
function constrainedScopeEval(obj: string): unknown { | ||
// eslint-disable-next-line @typescript-eslint/no-implied-eval, @typescript-eslint/no-unsafe-call | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. To be clear, // this is the no-implied-eval violationconstyoloFunction=Function('inline function');// this is the no-unsafe-call violationyoloFunction(); They just happen to be on the same line here. So, this isn't a case of these rules stepping on each others' toes IMO. | ||
return new Function(` | ||
"use strict"; | ||
var module = { exports: {} }; | ||
Uh oh!
There was an error while loading.Please reload this page.