From 1f557ae524c23bc1f1e698cfa512fd00904d7cc5 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Sun, 17 Dec 2023 22:14:45 -0500 Subject: [PATCH 1/3] fix(eslint-plugin): [no-unnecesary-type-assertion] treat unknown/any as nullable --- .../src/rules/no-unnecessary-condition.ts | 11 +++----- .../src/rules/prefer-nullish-coalescing.ts | 3 +-- .../no-unnecessary-type-assertion.test.ts | 5 ++++ packages/type-utils/src/predicates.ts | 25 +++++++------------ 4 files changed, 19 insertions(+), 25 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index a55fd0d3956a..0309eae196ae 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -530,7 +530,7 @@ export default createRule({ propertyType.value.toString(), ); if (propType) { - return isNullableType(propType, { allowUndefined: true }); + return isNullableType(propType); } } const typeName = getTypeName(checker, propertyType); @@ -568,14 +568,12 @@ export default createRule({ ); if (propType) { - return isNullableType(propType, { allowUndefined: true }); + return isNullableType(propType); } return !!checker.getIndexInfoOfType(type, ts.IndexKind.String); }); - return ( - !isOwnNullable && isNullableType(prevType, { allowUndefined: true }) - ); + return !isOwnNullable && isNullableType(prevType); } return false; } @@ -589,8 +587,7 @@ export default createRule({ const possiblyVoid = isTypeFlagSet(type, ts.TypeFlags.Void); return ( isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown) || - (isOwnNullable && - (isNullableType(type, { allowUndefined: true }) || possiblyVoid)) + (isOwnNullable && (isNullableType(type) || possiblyVoid)) ); } diff --git a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts index be498cb94ad0..c688d75d2771 100644 --- a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts +++ b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts @@ -309,8 +309,7 @@ export default createRule({ ): void { const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); const type = checker.getTypeAtLocation(tsNode.left); - const isNullish = isNullableType(type, { allowUndefined: true }); - if (!isNullish) { + if (!isNullableType(type)) { return; } diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts index 8cf362938bab..d4d2e11b7b7d 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts @@ -48,6 +48,11 @@ const foo = { 0: 'hello', 5: 'hello' } as PossibleTuple; ` let bar: number | undefined = x; let foo: number = bar!; + `, + ` +declare const a: { data?: unknown }; + +const x = a.data!; `, { code: ` diff --git a/packages/type-utils/src/predicates.ts b/packages/type-utils/src/predicates.ts index fdc79dc45ac0..1910d8d8d918 100644 --- a/packages/type-utils/src/predicates.ts +++ b/packages/type-utils/src/predicates.ts @@ -8,25 +8,18 @@ const log = debug('typescript-eslint:eslint-plugin:utils:types'); /** * Checks if the given type is (or accepts) nullable - * @param isReceiver true if the type is a receiving type (i.e. the type of a called function's parameter) */ -export function isNullableType( - type: ts.Type, - { - isReceiver = false, - allowUndefined = true, - }: { isReceiver?: boolean; allowUndefined?: boolean } = {}, -): boolean { +export function isNullableType(type: ts.Type): boolean { const flags = getTypeFlags(type); - if (isReceiver && flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) { - return true; - } - - if (allowUndefined) { - return (flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) !== 0; - } - return (flags & ts.TypeFlags.Null) !== 0; + return ( + (flags & + (ts.TypeFlags.Any | + ts.TypeFlags.Unknown | + ts.TypeFlags.Null | + ts.TypeFlags.Undefined)) !== + 0 + ); } /** From 83a7b44c29cf9b41d2711d646087e2367dba0031 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Mon, 18 Dec 2023 15:08:18 -0500 Subject: [PATCH 2/3] Use isTypeFlagSet --- packages/type-utils/src/predicates.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/type-utils/src/predicates.ts b/packages/type-utils/src/predicates.ts index 1910d8d8d918..f5b01022237e 100644 --- a/packages/type-utils/src/predicates.ts +++ b/packages/type-utils/src/predicates.ts @@ -2,7 +2,7 @@ import debug from 'debug'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; -import { getTypeFlags, isTypeFlagSet } from './typeFlagUtils'; +import { isTypeFlagSet } from './typeFlagUtils'; const log = debug('typescript-eslint:eslint-plugin:utils:types'); @@ -10,15 +10,12 @@ const log = debug('typescript-eslint:eslint-plugin:utils:types'); * Checks if the given type is (or accepts) nullable */ export function isNullableType(type: ts.Type): boolean { - const flags = getTypeFlags(type); - - return ( - (flags & - (ts.TypeFlags.Any | - ts.TypeFlags.Unknown | - ts.TypeFlags.Null | - ts.TypeFlags.Undefined)) !== - 0 + return isTypeFlagSet( + type, + ts.TypeFlags.Any | + ts.TypeFlags.Unknown | + ts.TypeFlags.Null | + ts.TypeFlags.Undefined, ); } From 948262fda69f1c39f51d7e35cd29f964827a7f41 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Thu, 11 Jan 2024 13:21:58 -0800 Subject: [PATCH 3/3] Update predicates.ts --- packages/type-utils/src/predicates.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/type-utils/src/predicates.ts b/packages/type-utils/src/predicates.ts index f5b01022237e..2fd45ac14598 100644 --- a/packages/type-utils/src/predicates.ts +++ b/packages/type-utils/src/predicates.ts @@ -9,7 +9,22 @@ const log = debug('typescript-eslint:eslint-plugin:utils:types'); /** * Checks if the given type is (or accepts) nullable */ -export function isNullableType(type: ts.Type): boolean { +export function isNullableType( + type: ts.Type, + { + isReceiver, + allowUndefined, + }?: { + /** + * @deprecated - this flag no longer does anything and will be removed in the next major + */ + isReceiver?: boolean; + /** + * @deprecated - this flag no longer does anything and will be removed in the next major + */ + allowUndefined?: boolean; + }, +): boolean { return isTypeFlagSet( type, ts.TypeFlags.Any |