diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 310dbacba3aae..60deee7e2c9fe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19882,6 +19882,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } if (sourceFlags & TypeFlags.Conditional) { + if ((source as ConditionalType).root === (target as ConditionalType).root) { + // Two instantiations of the same conditional type, just check instantiated outer type parameter equality + const params = (source as ConditionalType).root.outerTypeParameters || []; + const sourceTypeArguments = map(params, t => (source as ConditionalType).mapper ? getMappedType(t, (source as ConditionalType).mapper!) : t); + const targetTypeArguments = map(params, t => (target as ConditionalType).mapper ? getMappedType(t, (target as ConditionalType).mapper!) : t); + return typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, map(params, () => VarianceFlags.Unmeasurable), /*reportErrors*/ false, IntersectionState.None); + } if ((source as ConditionalType).root.isDistributive === (target as ConditionalType).root.isDistributive) { if (result = isRelatedTo((source as ConditionalType).checkType, (target as ConditionalType).checkType, RecursionFlags.Both, /*reportErrors*/ false)) { if (result &= isRelatedTo((source as ConditionalType).extendsType, (target as ConditionalType).extendsType, RecursionFlags.Both, /*reportErrors*/ false)) { @@ -26071,6 +26078,24 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { node.kind === SyntaxKind.PropertyDeclaration)!; } + function getControlFlowContainerForIdentifier(node: Identifier, declaration: Declaration, symbol: Symbol, typeFromSymbol: Type) { + // The declaration container is the innermost function that encloses the declaration of the variable + // or parameter. The flow container is the innermost function starting with which we analyze the control + // flow graph to determine the control flow based type. + const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter; + const declarationContainer = getControlFlowContainer(declaration); + let flowContainer = getControlFlowContainer(node); + // When the control flow originates in a function expression or arrow function and we are referencing + // a const variable or parameter from an outer function, we extend the origin of the control flow + // analysis to include the immediately enclosing function. + while (flowContainer !== declarationContainer && (flowContainer.kind === SyntaxKind.FunctionExpression || + flowContainer.kind === SyntaxKind.ArrowFunction || isObjectLiteralOrClassExpressionMethodOrAccessor(flowContainer)) && + (isConstVariable(symbol) && typeFromSymbol !== autoArrayType || isParameter && !isSymbolAssigned(symbol))) { + flowContainer = getControlFlowContainer(flowContainer); + } + return flowContainer; + } + // Check if a parameter or catch variable is assigned anywhere function isSymbolAssigned(symbol: Symbol) { if (!symbol.valueDeclaration) { @@ -26432,24 +26457,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } type = getNarrowableTypeForReference(type, node, checkMode); - - // The declaration container is the innermost function that encloses the declaration of the variable - // or parameter. The flow container is the innermost function starting with which we analyze the control - // flow graph to determine the control flow based type. const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter; const declarationContainer = getControlFlowContainer(declaration); - let flowContainer = getControlFlowContainer(node); - const isOuterVariable = flowContainer !== declarationContainer; - const isSpreadDestructuringAssignmentTarget = node.parent && node.parent.parent && isSpreadAssignment(node.parent) && isDestructuringAssignmentTarget(node.parent.parent); + const flowContainer = getControlFlowContainerForIdentifier(node, declaration, localOrExportSymbol, type); const isModuleExports = symbol.flags & SymbolFlags.ModuleExports; - // When the control flow originates in a function expression or arrow function and we are referencing - // a const variable or parameter from an outer function, we extend the origin of the control flow - // analysis to include the immediately enclosing function. - while (flowContainer !== declarationContainer && (flowContainer.kind === SyntaxKind.FunctionExpression || - flowContainer.kind === SyntaxKind.ArrowFunction || isObjectLiteralOrClassExpressionMethodOrAccessor(flowContainer)) && - (isConstVariable(localOrExportSymbol) && type !== autoArrayType || isParameter && !isSymbolAssigned(localOrExportSymbol))) { - flowContainer = getControlFlowContainer(flowContainer); - } + const isOuterVariable = getControlFlowContainer(node) !== declarationContainer; + const isSpreadDestructuringAssignmentTarget = node.parent && node.parent.parent && isSpreadAssignment(node.parent) && isDestructuringAssignmentTarget(node.parent.parent); // We only look for uninitialized variables in strict null checking mode, and only when we can analyze // the entire control flow graph from the variable's declaration (i.e. when the flow container and // declaration container are the same). @@ -32762,7 +32775,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function checkAssertionWorker(errNode: Node, type: TypeNode, expression: UnaryExpression | Expression, checkMode?: CheckMode) { - let exprType = checkExpression(expression, checkMode); + const exprType = checkExpression(expression, checkMode); if (isConstTypeReference(type)) { if (!isValidConstAssertionArgument(expression)) { error(expression, Diagnostics.A_const_assertions_can_only_be_applied_to_references_to_enum_members_or_string_number_boolean_array_or_object_literals); @@ -32770,15 +32783,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getRegularTypeOfLiteralType(exprType); } checkSourceElement(type); - exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(exprType)); const targetType = getTypeFromTypeNode(type); if (!isErrorType(targetType)) { addLazyDiagnostic(() => { - const widenedType = getWidenedType(exprType); + const regularType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(exprType)); + const widenedType = getWidenedType(regularType); if (!isTypeComparableTo(targetType, widenedType)) { - checkTypeComparableTo(exprType, targetType, errNode, + checkTypeComparableTo(regularType, targetType, errNode, Diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first); } + // Assertions, as a side effect, disable widening - some casts may rely on this, so + // if the expression type was widenable, we shouldn't assume the cast is extraneous. + // (Generally speaking, such casts are better served with `as const` casts nowadays though!) + else if (widenedType === exprType && isTypeIdenticalTo(targetType, widenedType)) { + errorOrSuggestion(!!compilerOptions.noUnnecessaryTypeAssertions, errNode, Diagnostics.Type_assertion_has_no_effect_on_the_type_of_this_expression); + } }); } return targetType; @@ -32791,8 +32810,38 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function checkNonNullAssertion(node: NonNullExpression) { - return node.flags & NodeFlags.OptionalChain ? checkNonNullChain(node as NonNullChain) : - getNonNullableType(checkExpression(node.expression)); + const exprType = checkExpression(node.expression); + const resultType = node.flags & NodeFlags.OptionalChain ? checkNonNullChain(node as NonNullChain) : getNonNullableType(exprType); + if (!isErrorType(resultType)) { + addLazyDiagnostic(() => { + if (isTypeIdenticalTo(exprType, resultType)) { + // A non-null assertion on an identifier may also suppress a used-before definition, so may still be useful even if the type is unchanged by it + // Identifiers on the left-hand-side of an assignment expression, ofc, can't do this, since they're for the assigned type itself. + if (isIdentifier(node.expression) && !containsUndefinedType(exprType) && !(isAssignmentExpression(node.parent) && node.parent.left === node)) { + const symbol = getSymbolAtLocation(node.expression); + const declaration = symbol?.valueDeclaration; + if (declaration) { + const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter; + if (!isParameter) { + const flowContainer = getControlFlowContainerForIdentifier(node.expression, declaration, symbol, exprType); + // We need a fake reference that isn't parented to the nonnull expression, since the non-null will affect control flow's result + // by suppressing the initial return type at the end of the flow + const fakeReference = factory.cloneNode(node.expression); + setParent(fakeReference, node.parent); + setTextRange(fakeReference, node.expression); + fakeReference.flowNode = node.expression.flowNode; + const flowType = getFlowTypeOfReference(fakeReference, exprType, undefinedType, flowContainer); + if (containsUndefinedType(flowType)) { + return; // Cast is required to suppress a use before assignment control flow error + } + } + } + } + errorOrSuggestion(!!compilerOptions.noUnnecessaryTypeAssertions, node, Diagnostics.Type_assertion_has_no_effect_on_the_type_of_this_expression); + } + }); + } + return resultType; } function checkExpressionWithTypeArguments(node: ExpressionWithTypeArguments | TypeQueryNode) { diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index aa0e7c7b91dbe..e198f3ab9466e 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -800,6 +800,15 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [ description: Diagnostics.Raise_an_error_when_a_function_parameter_isn_t_read, defaultValueDescription: false, }, + { + name: "noUnnecessaryTypeAssertions", + type: "boolean", + affectsSemanticDiagnostics: true, + affectsBuildInfo: true, + category: Diagnostics.Type_Checking, + description: Diagnostics.Raise_an_error_when_a_type_assertion_does_not_affect_the_type_of_an_expression, + defaultValueDescription: false, + }, { name: "exactOptionalPropertyTypes", type: "boolean", diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index c0395bdfea0a4..231efe1f85710 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5919,6 +5919,10 @@ "category": "Message", "code": 6803 }, + "Raise an error when a type assertion does not affect the type of an expression.": { + "category": "Message", + "code": 6804 + }, "one of:": { "category": "Message", @@ -6534,6 +6538,10 @@ "category": "Suggestion", "code": 80008 }, + "Type assertion has no effect on the type of this expression.": { + "category": "Error", + "code": 80009 + }, "Add missing 'super()' call": { "category": "Message", @@ -7392,6 +7400,14 @@ "category": "Message", "code": 95175 }, + "Remove unnecessary type assertion.": { + "category": "Message", + "code": 95176 + }, + "Remove all unnecessary type assertions.": { + "category": "Message", + "code": 95177 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b754fb57f944f..edc0861cd2879 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6713,6 +6713,7 @@ export interface CompilerOptions { noImplicitReturns?: boolean; noImplicitThis?: boolean; // Always combine with strict property noStrictGenericChecks?: boolean; + noUnnecessaryTypeAssertions?: boolean; noUnusedLocals?: boolean; noUnusedParameters?: boolean; noImplicitUseStrict?: boolean; diff --git a/src/services/_namespaces/ts.codefix.ts b/src/services/_namespaces/ts.codefix.ts index 23e971449be17..e3a04fa1e4b4b 100644 --- a/src/services/_namespaces/ts.codefix.ts +++ b/src/services/_namespaces/ts.codefix.ts @@ -66,6 +66,7 @@ export * from "../codefixes/wrapJsxInFragment"; export * from "../codefixes/convertToMappedObjectType"; export * from "../codefixes/removeAccidentalCallParentheses"; export * from "../codefixes/removeUnnecessaryAwait"; +export * from "../codefixes/removeUnnecessaryTypeAssertion"; export * from "../codefixes/splitTypeOnlyImport"; export * from "../codefixes/convertConstToLet"; export * from "../codefixes/fixExpectedComma"; diff --git a/src/services/codefixes/removeUnnecessaryTypeAssertion.ts b/src/services/codefixes/removeUnnecessaryTypeAssertion.ts new file mode 100644 index 0000000000000..9ba0aae3c20f5 --- /dev/null +++ b/src/services/codefixes/removeUnnecessaryTypeAssertion.ts @@ -0,0 +1,44 @@ +import { + Diagnostics, findAncestor, isAssertionExpression, isInJSDoc, Node, + isParenthesizedExpression, ParenthesizedExpression, SourceFile, textChanges, TextSpan, tryCast, HasJSDoc, first, last, needsParentheses, isNonNullExpression, AssertionExpression, NonNullExpression, +} from "../_namespaces/ts"; +import { codeFixAll, createCodeFixAction, findAncestorMatchingSpan, registerCodeFix } from "../_namespaces/ts.codefix"; + +const fixId = "removeUnnecessaryTypeAssertion"; +const errorCodes = [ + Diagnostics.Type_assertion_has_no_effect_on_the_type_of_this_expression.code, +]; + +registerCodeFix({ + errorCodes, + getCodeActions: function getCodeActionsToRemoveUnnecessaryTypeAssertion(context) { + const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span)); + if (changes.length > 0) { + return [createCodeFixAction(fixId, changes, Diagnostics.Remove_unnecessary_type_assertion, fixId, Diagnostics.Remove_all_unnecessary_type_assertions)]; + } + }, + fixIds: [fixId], + getAllCodeActions: context => { + return codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file, diag)); + }, +}); + +function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, span: TextSpan) { + let node: Node | undefined = findAncestorMatchingSpan(sourceFile, span); + if (node && isInJSDoc(node)) { + node = findAncestor(node, isParenthesizedExpression); + if (node) { + changeTracker.deleteNodeRange(sourceFile, first((node as HasJSDoc).jsDoc!), last((node as HasJSDoc).jsDoc!)); + if (!needsParentheses((node as ParenthesizedExpression).expression)) { + changeTracker.replaceNode(sourceFile, node, (node as ParenthesizedExpression).expression); + } + } + return; + } + const castExpr = tryCast(node, (n): n is AssertionExpression | NonNullExpression => isAssertionExpression(n) || isNonNullExpression(n)); + if (!castExpr) { + return; + } + + changeTracker.replaceNode(sourceFile, castExpr, castExpr.expression); +} diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 6a93d33726e1b..02f9243a02e01 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -7120,6 +7120,7 @@ declare namespace ts { noImplicitReturns?: boolean; noImplicitThis?: boolean; noStrictGenericChecks?: boolean; + noUnnecessaryTypeAssertions?: boolean; noUnusedLocals?: boolean; noUnusedParameters?: boolean; noImplicitUseStrict?: boolean; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index cc0b9416ac78e..881eea6e554b8 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3182,6 +3182,7 @@ declare namespace ts { noImplicitReturns?: boolean; noImplicitThis?: boolean; noStrictGenericChecks?: boolean; + noUnnecessaryTypeAssertions?: boolean; noUnusedLocals?: boolean; noUnusedParameters?: boolean; noImplicitUseStrict?: boolean; diff --git a/tests/baselines/reference/config/initTSConfig/Default initialized TSConfig/tsconfig.json b/tests/baselines/reference/config/initTSConfig/Default initialized TSConfig/tsconfig.json index 19844ec8065fc..9244faa2db279 100644 --- a/tests/baselines/reference/config/initTSConfig/Default initialized TSConfig/tsconfig.json +++ b/tests/baselines/reference/config/initTSConfig/Default initialized TSConfig/tsconfig.json @@ -87,6 +87,7 @@ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "noUnnecessaryTypeAssertions": true, /* Raise an error when a type assertion does not affect the type of an expression. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ diff --git a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with --help/tsconfig.json b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with --help/tsconfig.json index 19844ec8065fc..9244faa2db279 100644 --- a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with --help/tsconfig.json +++ b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with --help/tsconfig.json @@ -87,6 +87,7 @@ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "noUnnecessaryTypeAssertions": true, /* Raise an error when a type assertion does not affect the type of an expression. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ diff --git a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with --watch/tsconfig.json b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with --watch/tsconfig.json index 19844ec8065fc..9244faa2db279 100644 --- a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with --watch/tsconfig.json +++ b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with --watch/tsconfig.json @@ -87,6 +87,7 @@ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "noUnnecessaryTypeAssertions": true, /* Raise an error when a type assertion does not affect the type of an expression. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ diff --git a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with advanced options/tsconfig.json b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with advanced options/tsconfig.json index 6e28e016bd11d..26b5909435744 100644 --- a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with advanced options/tsconfig.json +++ b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with advanced options/tsconfig.json @@ -87,6 +87,7 @@ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "noUnnecessaryTypeAssertions": true, /* Raise an error when a type assertion does not affect the type of an expression. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ diff --git a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json index d73dbd191a220..a3380d3e0d0eb 100644 --- a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json +++ b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json @@ -87,6 +87,7 @@ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "noUnnecessaryTypeAssertions": true, /* Raise an error when a type assertion does not affect the type of an expression. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ diff --git a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with enum value compiler options/tsconfig.json b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with enum value compiler options/tsconfig.json index c4c554e65dffb..5eaec0897c451 100644 --- a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with enum value compiler options/tsconfig.json +++ b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with enum value compiler options/tsconfig.json @@ -87,6 +87,7 @@ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "noUnnecessaryTypeAssertions": true, /* Raise an error when a type assertion does not affect the type of an expression. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ diff --git a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with files options/tsconfig.json b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with files options/tsconfig.json index 2368ed9c35291..8b2d457572637 100644 --- a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with files options/tsconfig.json +++ b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with files options/tsconfig.json @@ -87,6 +87,7 @@ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "noUnnecessaryTypeAssertions": true, /* Raise an error when a type assertion does not affect the type of an expression. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ diff --git a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json index 195f29bcb8708..a94d99b9c2032 100644 --- a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json +++ b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json @@ -87,6 +87,7 @@ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "noUnnecessaryTypeAssertions": true, /* Raise an error when a type assertion does not affect the type of an expression. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ diff --git a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json index 19844ec8065fc..9244faa2db279 100644 --- a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json +++ b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json @@ -87,6 +87,7 @@ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "noUnnecessaryTypeAssertions": true, /* Raise an error when a type assertion does not affect the type of an expression. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ diff --git a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json index fe6fd06aa610a..09adb4642ec5e 100644 --- a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json +++ b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json @@ -87,6 +87,7 @@ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "noUnnecessaryTypeAssertions": true, /* Raise an error when a type assertion does not affect the type of an expression. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ diff --git a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with list compiler options/tsconfig.json b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with list compiler options/tsconfig.json index 12daecbe8ed51..0a8bf24c8070c 100644 --- a/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with list compiler options/tsconfig.json +++ b/tests/baselines/reference/config/initTSConfig/Initialized TSConfig with list compiler options/tsconfig.json @@ -87,6 +87,7 @@ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "noUnnecessaryTypeAssertions": true, /* Raise an error when a type assertion does not affect the type of an expression. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ diff --git a/tests/baselines/reference/config/showConfig/Shows tsconfig for single option/noUnnecessaryTypeAssertions/tsconfig.json b/tests/baselines/reference/config/showConfig/Shows tsconfig for single option/noUnnecessaryTypeAssertions/tsconfig.json new file mode 100644 index 0000000000000..27d971abf268e --- /dev/null +++ b/tests/baselines/reference/config/showConfig/Shows tsconfig for single option/noUnnecessaryTypeAssertions/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "noUnnecessaryTypeAssertions": true + } +} diff --git a/tests/baselines/reference/noUnnecessaryTypeAssertions1.errors.txt b/tests/baselines/reference/noUnnecessaryTypeAssertions1.errors.txt new file mode 100644 index 0000000000000..9110f61d9f9d4 --- /dev/null +++ b/tests/baselines/reference/noUnnecessaryTypeAssertions1.errors.txt @@ -0,0 +1,71 @@ +tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts(3,5): error TS80009: Type assertion has no effect on the type of this expression. +tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts(8,19): error TS80009: Type assertion has no effect on the type of this expression. +tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts(17,18): error TS80009: Type assertion has no effect on the type of this expression. +tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts(18,18): error TS80009: Type assertion has no effect on the type of this expression. +tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts(20,12): error TS80009: Type assertion has no effect on the type of this expression. +tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts(23,1): error TS80009: Type assertion has no effect on the type of this expression. +tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts(33,1): error TS2322: Type 'undefined' is not assignable to type '{}'. +tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts(35,1): error TS80009: Type assertion has no effect on the type of this expression. +tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts(39,23): error TS80009: Type assertion has no effect on the type of this expression. + + +==== tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts (9 errors) ==== + const a = {}; + let b: {}; + b = a as {}; + ~~~~~~~ +!!! error TS80009: Type assertion has no effect on the type of this expression. + interface Foo { + x?: string; + } + const foo1: Foo = { x: "ok" } as Foo; // cast technically erases type information, not a no-op + const foo2: Foo = foo1 as Foo; + ~~~~~~~~~~~ +!!! error TS80009: Type assertion has no effect on the type of this expression. + class A { + item: any; + } + class B { + item: any; + } + const aCls = new A(); + const bCls = new B(); + const aCls2: A = bCls as A; + ~~~~~~~~~ +!!! error TS80009: Type assertion has no effect on the type of this expression. + const bCls2: B = aCls as A; + ~~~~~~~~~ +!!! error TS80009: Type assertion has no effect on the type of this expression. + function foo(x: number): number { + return x!; + ~~ +!!! error TS80009: Type assertion has no effect on the type of this expression. + } + const a1 = 2; + a1?.toString!.call(2); + ~~~~~~~~~~~~~ +!!! error TS80009: Type assertion has no effect on the type of this expression. + function testRequired() { + let resolve: (value: unknown) => void + new Promise(resolve0 => { + resolve = resolve0 + }) + return resolve! // this non-null assertion is required, removing it causes compiler to hard fail + } + let x: unknown; + x! = 2; + x! = undefined; + ~~ +!!! error TS2322: Type 'undefined' is not assignable to type '{}'. + let y: number; + y! = 2; + ~~ +!!! error TS80009: Type assertion has no effect on the type of this expression. + + function generic(x: T, y: U) { + const a: object = x!; + const b: object = y!; + ~~ +!!! error TS80009: Type assertion has no effect on the type of this expression. + } + \ No newline at end of file diff --git a/tests/baselines/reference/noUnnecessaryTypeAssertions1.js b/tests/baselines/reference/noUnnecessaryTypeAssertions1.js new file mode 100644 index 0000000000000..72b5e2f64c1d8 --- /dev/null +++ b/tests/baselines/reference/noUnnecessaryTypeAssertions1.js @@ -0,0 +1,85 @@ +//// [noUnnecessaryTypeAssertions1.ts] +const a = {}; +let b: {}; +b = a as {}; +interface Foo { + x?: string; +} +const foo1: Foo = { x: "ok" } as Foo; // cast technically erases type information, not a no-op +const foo2: Foo = foo1 as Foo; +class A { + item: any; +} +class B { + item: any; +} +const aCls = new A(); +const bCls = new B(); +const aCls2: A = bCls as A; +const bCls2: B = aCls as A; +function foo(x: number): number { + return x!; +} +const a1 = 2; +a1?.toString!.call(2); +function testRequired() { + let resolve: (value: unknown) => void + new Promise(resolve0 => { + resolve = resolve0 + }) + return resolve! // this non-null assertion is required, removing it causes compiler to hard fail +} +let x: unknown; +x! = 2; +x! = undefined; +let y: number; +y! = 2; + +function generic(x: T, y: U) { + const a: object = x!; + const b: object = y!; +} + + +//// [noUnnecessaryTypeAssertions1.js] +"use strict"; +var a = {}; +var b; +b = a; +var foo1 = { x: "ok" }; // cast technically erases type information, not a no-op +var foo2 = foo1; +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +var B = /** @class */ (function () { + function B() { + } + return B; +}()); +var aCls = new A(); +var bCls = new B(); +var aCls2 = bCls; +var bCls2 = aCls; +function foo(x) { + return x; +} +var a1 = 2; +a1 === null || a1 === void 0 ? void 0 : a1.toString.call(2); +function testRequired() { + var resolve; + new Promise(function (resolve0) { + resolve = resolve0; + }); + return resolve; // this non-null assertion is required, removing it causes compiler to hard fail +} +var x; +x = 2; +x = undefined; +var y; +y = 2; +function generic(x, y) { + var a = x; + var b = y; +} diff --git a/tests/baselines/reference/noUnnecessaryTypeAssertions1.symbols b/tests/baselines/reference/noUnnecessaryTypeAssertions1.symbols new file mode 100644 index 0000000000000..f2f21646f78df --- /dev/null +++ b/tests/baselines/reference/noUnnecessaryTypeAssertions1.symbols @@ -0,0 +1,131 @@ +=== tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts === +const a = {}; +>a : Symbol(a, Decl(noUnnecessaryTypeAssertions1.ts, 0, 5)) + +let b: {}; +>b : Symbol(b, Decl(noUnnecessaryTypeAssertions1.ts, 1, 3)) + +b = a as {}; +>b : Symbol(b, Decl(noUnnecessaryTypeAssertions1.ts, 1, 3)) +>a : Symbol(a, Decl(noUnnecessaryTypeAssertions1.ts, 0, 5)) + +interface Foo { +>Foo : Symbol(Foo, Decl(noUnnecessaryTypeAssertions1.ts, 2, 12)) + + x?: string; +>x : Symbol(Foo.x, Decl(noUnnecessaryTypeAssertions1.ts, 3, 15)) +} +const foo1: Foo = { x: "ok" } as Foo; // cast technically erases type information, not a no-op +>foo1 : Symbol(foo1, Decl(noUnnecessaryTypeAssertions1.ts, 6, 5)) +>Foo : Symbol(Foo, Decl(noUnnecessaryTypeAssertions1.ts, 2, 12)) +>x : Symbol(x, Decl(noUnnecessaryTypeAssertions1.ts, 6, 19)) +>Foo : Symbol(Foo, Decl(noUnnecessaryTypeAssertions1.ts, 2, 12)) + +const foo2: Foo = foo1 as Foo; +>foo2 : Symbol(foo2, Decl(noUnnecessaryTypeAssertions1.ts, 7, 5)) +>Foo : Symbol(Foo, Decl(noUnnecessaryTypeAssertions1.ts, 2, 12)) +>foo1 : Symbol(foo1, Decl(noUnnecessaryTypeAssertions1.ts, 6, 5)) +>Foo : Symbol(Foo, Decl(noUnnecessaryTypeAssertions1.ts, 2, 12)) + +class A { +>A : Symbol(A, Decl(noUnnecessaryTypeAssertions1.ts, 7, 30)) + + item: any; +>item : Symbol(A.item, Decl(noUnnecessaryTypeAssertions1.ts, 8, 9)) +} +class B { +>B : Symbol(B, Decl(noUnnecessaryTypeAssertions1.ts, 10, 1)) + + item: any; +>item : Symbol(B.item, Decl(noUnnecessaryTypeAssertions1.ts, 11, 9)) +} +const aCls = new A(); +>aCls : Symbol(aCls, Decl(noUnnecessaryTypeAssertions1.ts, 14, 5)) +>A : Symbol(A, Decl(noUnnecessaryTypeAssertions1.ts, 7, 30)) + +const bCls = new B(); +>bCls : Symbol(bCls, Decl(noUnnecessaryTypeAssertions1.ts, 15, 5)) +>B : Symbol(B, Decl(noUnnecessaryTypeAssertions1.ts, 10, 1)) + +const aCls2: A = bCls as A; +>aCls2 : Symbol(aCls2, Decl(noUnnecessaryTypeAssertions1.ts, 16, 5)) +>A : Symbol(A, Decl(noUnnecessaryTypeAssertions1.ts, 7, 30)) +>bCls : Symbol(bCls, Decl(noUnnecessaryTypeAssertions1.ts, 15, 5)) +>A : Symbol(A, Decl(noUnnecessaryTypeAssertions1.ts, 7, 30)) + +const bCls2: B = aCls as A; +>bCls2 : Symbol(bCls2, Decl(noUnnecessaryTypeAssertions1.ts, 17, 5)) +>B : Symbol(B, Decl(noUnnecessaryTypeAssertions1.ts, 10, 1)) +>aCls : Symbol(aCls, Decl(noUnnecessaryTypeAssertions1.ts, 14, 5)) +>A : Symbol(A, Decl(noUnnecessaryTypeAssertions1.ts, 7, 30)) + +function foo(x: number): number { +>foo : Symbol(foo, Decl(noUnnecessaryTypeAssertions1.ts, 17, 27)) +>x : Symbol(x, Decl(noUnnecessaryTypeAssertions1.ts, 18, 13)) + + return x!; +>x : Symbol(x, Decl(noUnnecessaryTypeAssertions1.ts, 18, 13)) +} +const a1 = 2; +>a1 : Symbol(a1, Decl(noUnnecessaryTypeAssertions1.ts, 21, 5)) + +a1?.toString!.call(2); +>a1?.toString!.call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --)) +>a1?.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>a1 : Symbol(a1, Decl(noUnnecessaryTypeAssertions1.ts, 21, 5)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --)) + +function testRequired() { +>testRequired : Symbol(testRequired, Decl(noUnnecessaryTypeAssertions1.ts, 22, 22)) + + let resolve: (value: unknown) => void +>resolve : Symbol(resolve, Decl(noUnnecessaryTypeAssertions1.ts, 24, 7)) +>value : Symbol(value, Decl(noUnnecessaryTypeAssertions1.ts, 24, 18)) + + new Promise(resolve0 => { +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>resolve0 : Symbol(resolve0, Decl(noUnnecessaryTypeAssertions1.ts, 25, 16)) + + resolve = resolve0 +>resolve : Symbol(resolve, Decl(noUnnecessaryTypeAssertions1.ts, 24, 7)) +>resolve0 : Symbol(resolve0, Decl(noUnnecessaryTypeAssertions1.ts, 25, 16)) + + }) + return resolve! // this non-null assertion is required, removing it causes compiler to hard fail +>resolve : Symbol(resolve, Decl(noUnnecessaryTypeAssertions1.ts, 24, 7)) +} +let x: unknown; +>x : Symbol(x, Decl(noUnnecessaryTypeAssertions1.ts, 30, 3)) + +x! = 2; +>x : Symbol(x, Decl(noUnnecessaryTypeAssertions1.ts, 30, 3)) + +x! = undefined; +>x : Symbol(x, Decl(noUnnecessaryTypeAssertions1.ts, 30, 3)) +>undefined : Symbol(undefined) + +let y: number; +>y : Symbol(y, Decl(noUnnecessaryTypeAssertions1.ts, 33, 3)) + +y! = 2; +>y : Symbol(y, Decl(noUnnecessaryTypeAssertions1.ts, 33, 3)) + +function generic(x: T, y: U) { +>generic : Symbol(generic, Decl(noUnnecessaryTypeAssertions1.ts, 34, 7)) +>T : Symbol(T, Decl(noUnnecessaryTypeAssertions1.ts, 36, 17)) +>U : Symbol(U, Decl(noUnnecessaryTypeAssertions1.ts, 36, 41)) +>x : Symbol(x, Decl(noUnnecessaryTypeAssertions1.ts, 36, 60)) +>T : Symbol(T, Decl(noUnnecessaryTypeAssertions1.ts, 36, 17)) +>y : Symbol(y, Decl(noUnnecessaryTypeAssertions1.ts, 36, 65)) +>U : Symbol(U, Decl(noUnnecessaryTypeAssertions1.ts, 36, 41)) + + const a: object = x!; +>a : Symbol(a, Decl(noUnnecessaryTypeAssertions1.ts, 37, 9)) +>x : Symbol(x, Decl(noUnnecessaryTypeAssertions1.ts, 36, 60)) + + const b: object = y!; +>b : Symbol(b, Decl(noUnnecessaryTypeAssertions1.ts, 38, 9)) +>y : Symbol(y, Decl(noUnnecessaryTypeAssertions1.ts, 36, 65)) +} + diff --git a/tests/baselines/reference/noUnnecessaryTypeAssertions1.types b/tests/baselines/reference/noUnnecessaryTypeAssertions1.types new file mode 100644 index 0000000000000..e789de95b7e37 --- /dev/null +++ b/tests/baselines/reference/noUnnecessaryTypeAssertions1.types @@ -0,0 +1,148 @@ +=== tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts === +const a = {}; +>a : {} +>{} : {} + +let b: {}; +>b : {} + +b = a as {}; +>b = a as {} : {} +>b : {} +>a as {} : {} +>a : {} + +interface Foo { + x?: string; +>x : string | undefined +} +const foo1: Foo = { x: "ok" } as Foo; // cast technically erases type information, not a no-op +>foo1 : Foo +>{ x: "ok" } as Foo : Foo +>{ x: "ok" } : { x: string; } +>x : string +>"ok" : "ok" + +const foo2: Foo = foo1 as Foo; +>foo2 : Foo +>foo1 as Foo : Foo +>foo1 : Foo + +class A { +>A : A + + item: any; +>item : any +} +class B { +>B : B + + item: any; +>item : any +} +const aCls = new A(); +>aCls : A +>new A() : A +>A : typeof A + +const bCls = new B(); +>bCls : B +>new B() : B +>B : typeof B + +const aCls2: A = bCls as A; +>aCls2 : A +>bCls as A : A +>bCls : B + +const bCls2: B = aCls as A; +>bCls2 : B +>aCls as A : A +>aCls : A + +function foo(x: number): number { +>foo : (x: number) => number +>x : number + + return x!; +>x! : number +>x : number +} +const a1 = 2; +>a1 : 2 +>2 : 2 + +a1?.toString!.call(2); +>a1?.toString!.call(2) : string +>a1?.toString!.call : (this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R +>a1?.toString! : (radix?: number | undefined) => string +>a1?.toString : (radix?: number | undefined) => string +>a1 : 2 +>toString : (radix?: number | undefined) => string +>call : (this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R +>2 : 2 + +function testRequired() { +>testRequired : () => (value: unknown) => void + + let resolve: (value: unknown) => void +>resolve : (value: unknown) => void +>value : unknown + + new Promise(resolve0 => { +>new Promise(resolve0 => { resolve = resolve0 }) : Promise +>Promise : PromiseConstructor +>resolve0 => { resolve = resolve0 } : (resolve0: (value: unknown) => void) => void +>resolve0 : (value: unknown) => void + + resolve = resolve0 +>resolve = resolve0 : (value: unknown) => void +>resolve : (value: unknown) => void +>resolve0 : (value: unknown) => void + + }) + return resolve! // this non-null assertion is required, removing it causes compiler to hard fail +>resolve! : (value: unknown) => void +>resolve : (value: unknown) => void +} +let x: unknown; +>x : unknown + +x! = 2; +>x! = 2 : 2 +>x! : {} +>x : unknown +>2 : 2 + +x! = undefined; +>x! = undefined : undefined +>x! : {} +>x : unknown +>undefined : undefined + +let y: number; +>y : number + +y! = 2; +>y! = 2 : 2 +>y! : number +>y : number +>2 : 2 + +function generic(x: T, y: U) { +>generic : (x: T, y: U) => void +>null : null +>x : T +>y : U + + const a: object = x!; +>a : object +>x! : object +>x : object | null + + const b: object = y!; +>b : object +>y! : U +>y : U +} + diff --git a/tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts b/tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts new file mode 100644 index 0000000000000..68bee24c5e792 --- /dev/null +++ b/tests/cases/conformance/additionalChecks/noUnnecessaryTypeAssertions1.ts @@ -0,0 +1,43 @@ +// @noUnnecessaryTypeAssertions: true +// @strict: true +// @lib: es6 +const a = {}; +let b: {}; +b = a as {}; +interface Foo { + x?: string; +} +const foo1: Foo = { x: "ok" } as Foo; // cast technically erases type information, not a no-op +const foo2: Foo = foo1 as Foo; +class A { + item: any; +} +class B { + item: any; +} +const aCls = new A(); +const bCls = new B(); +const aCls2: A = bCls as A; +const bCls2: B = aCls as A; +function foo(x: number): number { + return x!; +} +const a1 = 2; +a1?.toString!.call(2); +function testRequired() { + let resolve: (value: unknown) => void + new Promise(resolve0 => { + resolve = resolve0 + }) + return resolve! // this non-null assertion is required, removing it causes compiler to hard fail +} +let x: unknown; +x! = 2; +x! = undefined; +let y: number; +y! = 2; + +function generic(x: T, y: U) { + const a: object = x!; + const b: object = y!; +} diff --git a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts index 1168b9d4c3e59..95b3c2db67bd6 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts @@ -2,7 +2,7 @@ ////class A { //// method(a: number, b: string): boolean; -//// method(a: string | number, b?: string | number): boolean | Function { return a + b as any; } +//// method(a: string | number, b?: string | number): boolean | Function { return a + b; } ////} ////class C implements A {} @@ -11,7 +11,7 @@ verify.codeFix({ newFileContent: `class A { method(a: number, b: string): boolean; - method(a: string | number, b?: string | number): boolean | Function { return a + b as any; } + method(a: string | number, b?: string | number): boolean | Function { return a + b; } } class C implements A { method(a: number, b: string): boolean; diff --git a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts index 5da060d3d365e..c7aef509be38c 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts @@ -4,7 +4,7 @@ //// method(a: any, b: string): boolean; //// method(a: string, b: number): Function; //// method(a: string): Function; -//// method(a: string | number, b?: string | number): boolean | Function { return a + b as any; } +//// method(a: string | number, b?: string | number): boolean | Function { return a + b; } ////} ////class C implements A { } @@ -15,7 +15,7 @@ verify.codeFix({ method(a: any, b: string): boolean; method(a: string, b: number): Function; method(a: string): Function; - method(a: string | number, b?: string | number): boolean | Function { return a + b as any; } + method(a: string | number, b?: string | number): boolean | Function { return a + b; } } class C implements A { method(a: any, b: string): boolean; diff --git a/tests/cases/fourslash/codeFixRemoveUnnecessaryTypeAssertions1.ts b/tests/cases/fourslash/codeFixRemoveUnnecessaryTypeAssertions1.ts new file mode 100644 index 0000000000000..a0a7085285ce6 --- /dev/null +++ b/tests/cases/fourslash/codeFixRemoveUnnecessaryTypeAssertions1.ts @@ -0,0 +1,70 @@ +/// +////const a = {}; +////let b: {}; +////b = a as {}; +////interface Foo { +//// x?: string; +////} +////const foo1: Foo = {x: "ok"} as Foo; // cast technically erases type information, not a no-op +////const foo2: Foo = foo1 as Foo; +////class A { +//// item: any; +////} +////class B { +//// item: any; +////} +////const aCls = new A(); +////const bCls = new B(); +////const aCls2: A = bCls as A; +////const bCls2: B = aCls as A; +////const tup = [0] as [0]; // required to preserve literalness + +verify.codeFix({ + description: ts.Diagnostics.Remove_unnecessary_type_assertion.message, + index: 0, + newFileContent: +`const a = {}; +let b: {}; +b = a; +interface Foo { + x?: string; +} +const foo1: Foo = {x: "ok"} as Foo; // cast technically erases type information, not a no-op +const foo2: Foo = foo1 as Foo; +class A { + item: any; +} +class B { + item: any; +} +const aCls = new A(); +const bCls = new B(); +const aCls2: A = bCls as A; +const bCls2: B = aCls as A; +const tup = [0] as [0]; // required to preserve literalness` +}); + +verify.codeFixAll({ + fixAllDescription: ts.Diagnostics.Remove_all_unnecessary_type_assertions.message, + fixId: "removeUnnecessaryTypeAssertion", + newFileContent: +`const a = {}; +let b: {}; +b = a; +interface Foo { + x?: string; +} +const foo1: Foo = {x: "ok"} as Foo; // cast technically erases type information, not a no-op +const foo2: Foo = foo1; +class A { + item: any; +} +class B { + item: any; +} +const aCls = new A(); +const bCls = new B(); +const aCls2: A = bCls; +const bCls2: B = aCls; +const tup = [0] as [0]; // required to preserve literalness` +}); diff --git a/tests/cases/fourslash/codeFixRemoveUnnecessaryTypeAssertions2.ts b/tests/cases/fourslash/codeFixRemoveUnnecessaryTypeAssertions2.ts new file mode 100644 index 0000000000000..1fde493f15d2a --- /dev/null +++ b/tests/cases/fourslash/codeFixRemoveUnnecessaryTypeAssertions2.ts @@ -0,0 +1,67 @@ +/// +////const a = {}; +////let b: {}; +////b = <{}>a; +////interface Foo { +//// x?: string; +////} +////const foo1: Foo = {x: "ok"}; // cast technically erases type information, not a no-op +////const foo2: Foo = foo1; +////class A { +//// item: any; +////} +////class B { +//// item: any; +////} +////const aCls = new A(); +////const bCls = new B(); +////const aCls2: A = bCls; +////const bCls2: B = aCls; + +verify.codeFix({ + description: ts.Diagnostics.Remove_unnecessary_type_assertion.message, + index: 0, + newFileContent: +`const a = {}; +let b: {}; +b = a; +interface Foo { + x?: string; +} +const foo1: Foo = {x: "ok"}; // cast technically erases type information, not a no-op +const foo2: Foo = foo1; +class A { + item: any; +} +class B { + item: any; +} +const aCls = new A(); +const bCls = new B(); +const aCls2: A = bCls; +const bCls2: B = aCls;` +}); + +verify.codeFixAll({ + fixAllDescription: ts.Diagnostics.Remove_all_unnecessary_type_assertions.message, + fixId: "removeUnnecessaryTypeAssertion", + newFileContent: +`const a = {}; +let b: {}; +b = a; +interface Foo { + x?: string; +} +const foo1: Foo = {x: "ok"}; // cast technically erases type information, not a no-op +const foo2: Foo = foo1; +class A { + item: any; +} +class B { + item: any; +} +const aCls = new A(); +const bCls = new B(); +const aCls2: A = bCls; +const bCls2: B = aCls;` +}); diff --git a/tests/cases/fourslash/codeFixRemoveUnnecessaryTypeAssertions3.ts b/tests/cases/fourslash/codeFixRemoveUnnecessaryTypeAssertions3.ts new file mode 100644 index 0000000000000..6cf7881ec9514 --- /dev/null +++ b/tests/cases/fourslash/codeFixRemoveUnnecessaryTypeAssertions3.ts @@ -0,0 +1,101 @@ +/// +// @checkJs: true +// @Filename: /types.d.ts +////interface Foo { +//// x?: string; +////} +////declare class A { +//// item: any; +////} +////declare class B { +//// item: any; +////} +// @Filename: /file.js +////const a = {}; +/////** +//// * @type {{}} +//// */ +////let b; +////b = /** @type {{}} */(a); +/////** +//// * @type {Foo} +//// */ +////const foo1 = /** @type {Foo} */({x: "ok"}); // cast technically erases type information, not a no-op +/////** +//// * @type {Foo} +//// */ +////const foo2 = /** @type {Foo} */(foo1); +////const aCls = new A(); +////const bCls = new B(); +/////** +//// * @type {A} +//// */ +////const aCls2 = /** @type {A} */(bCls); +/////** +//// * @type {B} +//// */ +////const bCls2 = /** @type {A} */(aCls); +////const fn = () => /** @type {{}} */({}); + +goTo.file(1); +verify.codeFix({ + description: ts.Diagnostics.Remove_unnecessary_type_assertion.message, + index: 2, + newFileContent: +`const a = {}; +/** + * @type {{}} + */ +let b; +b = a; +/** + * @type {Foo} + */ +const foo1 = /** @type {Foo} */({x: "ok"}); // cast technically erases type information, not a no-op +/** + * @type {Foo} + */ +const foo2 = /** @type {Foo} */(foo1); +const aCls = new A(); +const bCls = new B(); +/** + * @type {A} + */ +const aCls2 = /** @type {A} */(bCls); +/** + * @type {B} + */ +const bCls2 = /** @type {A} */(aCls); +const fn = () => /** @type {{}} */({});` +}); + +verify.codeFixAll({ + fixAllDescription: ts.Diagnostics.Remove_all_unnecessary_type_assertions.message, + fixId: "removeUnnecessaryTypeAssertion", + newFileContent: +`const a = {}; +/** + * @type {{}} + */ +let b; +b = a; +/** + * @type {Foo} + */ +const foo1 = /** @type {Foo} */({x: "ok"}); // cast technically erases type information, not a no-op +/** + * @type {Foo} + */ +const foo2 = foo1; +const aCls = new A(); +const bCls = new B(); +/** + * @type {A} + */ +const aCls2 = bCls; +/** + * @type {B} + */ +const bCls2 = aCls; +const fn = () => /** @type {{}} */({});` +}); diff --git a/tests/cases/fourslash/codeFixRemoveUnnecessaryTypeAssertions4.ts b/tests/cases/fourslash/codeFixRemoveUnnecessaryTypeAssertions4.ts new file mode 100644 index 0000000000000..1d4146ec292ea --- /dev/null +++ b/tests/cases/fourslash/codeFixRemoveUnnecessaryTypeAssertions4.ts @@ -0,0 +1,25 @@ +////function foo(x: number): number { +//// return x!; +////} +////const a = 2; +////a?.toString!.call(2); + +verify.codeFix({ + description: ts.Diagnostics.Remove_unnecessary_type_assertion.message, + index: 0, + newFileContent: `function foo(x: number): number { + return x; +} +const a = 2; +a?.toString!.call(2);` +}); + +verify.codeFixAll({ + fixAllDescription: ts.Diagnostics.Remove_all_unnecessary_type_assertions.message, + fixId: "removeUnnecessaryTypeAssertion", + newFileContent: `function foo(x: number): number { + return x; +} +const a = 2; +a?.toString.call(2);` +}); \ No newline at end of file