Add eslint rule for disallowing dangerous type assertions (#211669)

This adds a new (off by default) eslint rule that disables two specific types of dangerous type assertions:

```ts
<Type>{...};
{...} as Type;
```

These are bad because they can easily hide missing properties and strictness errors

Right now just adding this rule but will assign out fixes to owners in different areas of the code
This commit is contained in:
Matt Bierner 2024-04-30 11:27:22 -07:00 committed by GitHub
parent 431afbe459
commit 001929db91
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 46 additions and 5 deletions

View File

@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as eslint from 'eslint';
import { TSESTree } from '@typescript-eslint/experimental-utils';
export = new class NoDangerousTypeAssertions implements eslint.Rule.RuleModule {
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
// Disable in tests for now
if (context.getFilename().includes('.test')) {
return {};
}
return {
// Disallow type assertions on object literals: <T>{ ... } or {} as T
['TSTypeAssertion > ObjectExpression, TSAsExpression > ObjectExpression']: (node: any) => {
const objectNode = node as TSESTree.Node;
const parent = objectNode.parent as TSESTree.TSTypeAssertion | TSESTree.TSAsExpression;
if (
// Allow `as const` assertions
(parent.typeAnnotation.type === 'TSTypeReference' && parent.typeAnnotation.typeName.type === 'Identifier' && parent.typeAnnotation.typeName.name === 'cost')
// For also now still allow `any` casts
|| (parent.typeAnnotation.type === 'TSAnyKeyword')
) {
return;
}
context.report({
node,
message: "Don't use type assertions for creating objects as this can hide type errors."
});
},
};
}
};

View File

@ -73,6 +73,7 @@
"local/code-parameter-properties-must-have-explicit-accessibility": "warn",
"local/code-no-nls-in-standalone-editor": "warn",
"local/code-no-potentially-unsafe-disposables": "warn",
"local/code-no-dangerous-type-assertions": "off",
"local/code-no-standalone-editor": "warn",
"local/code-no-unexternalized-strings": "warn",
"local/code-must-use-super-dispose": "warn",

View File

@ -246,7 +246,7 @@ export class FoldingRegions {
}
public toFoldRange(index: number): FoldRange {
return <FoldRange>{
return {
startLineNumber: this._startIndexes[index] & MAX_LINE_NUMBER,
endLineNumber: this._endIndexes[index] & MAX_LINE_NUMBER,
type: this._types ? this._types[index] : undefined,

View File

@ -370,7 +370,7 @@ export function registerChatCodeBlockActions() {
const editorService = accessor.get(IEditorService);
const chatService = accessor.get(IChatService);
editorService.openEditor(<IUntitledTextResourceEditorInput>{ contents: context.code, languageId: context.languageId, resource: undefined });
editorService.openEditor({ contents: context.code, languageId: context.languageId, resource: undefined } satisfies IUntitledTextResourceEditorInput);
if (isResponseVM(context.element)) {
chatService.notifyUserAction({

View File

@ -59,7 +59,7 @@ export class MarkersFilters extends Disposable {
set excludedFiles(filesExclude: boolean) {
if (this._excludedFiles.get() !== filesExclude) {
this._excludedFiles.set(filesExclude);
this._onDidChange.fire(<IMarkersFiltersChangeEvent>{ excludedFiles: true });
this._onDidChange.fire({ excludedFiles: true });
}
}
@ -70,7 +70,7 @@ export class MarkersFilters extends Disposable {
set activeFile(activeFile: boolean) {
if (this._activeFile.get() !== activeFile) {
this._activeFile.set(activeFile);
this._onDidChange.fire(<IMarkersFiltersChangeEvent>{ activeFile: true });
this._onDidChange.fire({ activeFile: true });
}
}
@ -81,7 +81,7 @@ export class MarkersFilters extends Disposable {
set showWarnings(showWarnings: boolean) {
if (this._showWarnings.get() !== showWarnings) {
this._showWarnings.set(showWarnings);
this._onDidChange.fire(<IMarkersFiltersChangeEvent>{ showWarnings: true });
this._onDidChange.fire({ showWarnings: true });
}
}