diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index eb23835ffc4..fce41c663a1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10631,6 +10631,22 @@ namespace ts { target = getSimplifiedType(target); } + // Try to see if we're relating something like `Foo` -> `Bar | null | undefined`. + // If so, reporting the `null` and `undefined` in the type is hardly useful. + // First, see if we're even relating an atomic type to a union. + // Then see if the target is stripped down to a single non-union type. + // We actually want to remove null and undefined naively here (rather than getNonNullableType), + // since we don't want to end up with a worse error like "`Foo` is not assignable to `NonNullable`" + // when dealing with generics. + if (target.flags & TypeFlags.Union && + source.flags & ((TypeFlags.Primitive | TypeFlags.Object) & ~(TypeFlags.Nullable | TypeFlags.Void)) && + (target as UnionType).types.length <= 3 && maybeTypeOfKind(target, TypeFlags.Nullable)) { + const nullStrippedTarget = extractTypesOfKind(target, ~TypeFlags.Nullable); + if (!(nullStrippedTarget.flags & (TypeFlags.Union | TypeFlags.Never))) { + target = nullStrippedTarget; + } + } + // both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases if (source === target) return Ternary.True; @@ -12223,7 +12239,7 @@ namespace ts { if (deferredGlobalNonNullableTypeAlias !== unknownSymbol) { return getTypeAliasInstantiation(deferredGlobalNonNullableTypeAlias, [type]); } - return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); // Type alias unavailable, fall back to non-higherorder behavior + return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); // Type alias unavailable, fall back to non-higher-order behavior } function getNonNullableType(type: Type): Type {