From eedc389c747f70bd3c2c46e68d6313c6470c437c Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 26 Mar 2020 02:02:13 -0700 Subject: [PATCH] Better assignability errors when the target is an intersection that gets reduced to 'never'. --- src/compiler/checker.ts | 12 +++++++++++- src/compiler/diagnosticMessages.json | 4 ++++ src/compiler/types.ts | 4 +++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 59bbab5eb5e..cca0e55ddaf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4102,7 +4102,9 @@ namespace ts { return undefined!; // TODO: GH#18217 } - type = getReducedType(type); + if (!(context.flags & NodeBuilderFlags.PreserveVacuousIntersections)) { + type = getReducedType(type); + } if (type.flags & TypeFlags.Any) { context.approximateLength += 3; @@ -15528,6 +15530,10 @@ namespace ts { return result; } } + else if (getObjectFlags(originalTarget) & ObjectFlags.IsNeverIntersection) { + const intersectionString = typeToString(originalTarget, /*enclosingDeclaration*/ undefined, TypeFormatFlags.PreserveVacuousIntersections); + errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.The_type_never_was_reduced_from_the_intersection_0_Each_type_of_that_intersection_has_properties_that_conflict_so_values_of_that_type_can_never_exist, intersectionString); + } if (!headMessage && maybeSuppress) { lastSkippedInfo = [source, target]; // Used by, eg, missing property checking to replace the top-level message with a more informative one @@ -24098,6 +24104,10 @@ namespace ts { relatedInfo = suggestion.valueDeclaration && createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestedName); } else { + if (getObjectFlags(containingType) & ObjectFlags.IsNeverIntersection) { + const intersectionString = typeToString(containingType, /*enclosingDeclaration*/ undefined, TypeFormatFlags.PreserveVacuousIntersections); + errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.The_type_never_was_reduced_from_the_intersection_0_Each_type_of_that_intersection_has_properties_that_conflict_so_values_of_that_type_can_never_exist, intersectionString); + } errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType)); } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index d0d7b1f7785..152c736d07f 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5653,5 +5653,9 @@ "An optional chain cannot contain private identifiers.": { "category": "Error", "code": 18030 + }, + "The type 'never' was reduced from the intersection '{0}'. Each type of that intersection has properties that conflict, so values of that type can never exist.": { + "category": "Error", + "code": 18031 } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8b6f5f8fa5a..873863585dd 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3687,6 +3687,7 @@ namespace ts { OmitParameterModifiers = 1 << 13, // Omit modifiers on parameters UseAliasDefinedOutsideCurrentScope = 1 << 14, // Allow non-visible aliases UseSingleQuotesForStringLiteralType = 1 << 28, // Use single quotes for string literal type + PreserveVacuousIntersections = 1 << 29, // uh oh... // Error handling AllowThisInObjectLiteral = 1 << 15, @@ -3730,6 +3731,7 @@ namespace ts { UseAliasDefinedOutsideCurrentScope = 1 << 14, // For a `type T = ... ` defined in a different file, write `T` instead of its value, even though `T` can't be accessed in the current scope. UseSingleQuotesForStringLiteralType = 1 << 28, // Use single quotes for string literal type + PreserveVacuousIntersections = 1 << 29, // uh oh // Error Handling AllowUniqueESSymbolType = 1 << 20, // This is bit 20 to align with the same bit in `NodeBuilderFlags` @@ -3749,7 +3751,7 @@ namespace ts { NodeBuilderFlagsMask = NoTruncation | WriteArrayAsGenericType | UseStructuralFallback | WriteTypeArgumentsOfSignature | UseFullyQualifiedType | SuppressAnyReturnType | MultilineObjectLiterals | WriteClassExpressionAsTypeLiteral | UseTypeOfFunction | OmitParameterModifiers | UseAliasDefinedOutsideCurrentScope | AllowUniqueESSymbolType | InTypeAlias | - UseSingleQuotesForStringLiteralType, + UseSingleQuotesForStringLiteralType | PreserveVacuousIntersections, } export const enum SymbolFormatFlags {