diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a9e4ac7293f5a..3e1babb34b9c3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -74,7 +74,7 @@ namespace ts { TypeofNEString = 1 << 8, // typeof x !== "string" TypeofNENumber = 1 << 9, // typeof x !== "number" TypeofNEBigInt = 1 << 10, // typeof x !== "bigint" - TypeofNEBoolean = 1 << 11, // typeof x !== "boolean" + TypeofNEBoolean = 1 << 11, // typeof x !== "boolean" TypeofNESymbol = 1 << 12, // typeof x !== "symbol" TypeofNEObject = 1 << 13, // typeof x !== "object" TypeofNEFunction = 1 << 14, // typeof x !== "function" @@ -87,7 +87,10 @@ namespace ts { NEUndefinedOrNull = 1 << 21, // x != undefined / x != null Truthy = 1 << 22, // x Falsy = 1 << 23, // !x - All = (1 << 24) - 1, + IsUndefined = 1 << 24, // Exactly undefined + IsNull = 1 << 25, // Exactly null + IsUndefinedOrNull = IsUndefined | IsNull, + All = (1 << 27) - 1, // The following members encode facts about particular kinds of types for use in the getTypeFacts function. // The presence of a particular fact means that the given test is true for some (and possibly all) values // of that kind of type. @@ -129,11 +132,13 @@ namespace ts { ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy, - NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, - EmptyObjectStrictFacts = All & ~(EQUndefined | EQNull | EQUndefinedOrNull), + VoidFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy, + UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy | IsUndefined, + NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy | IsNull, + EmptyObjectStrictFacts = All & ~(EQUndefined | EQNull | EQUndefinedOrNull | IsUndefinedOrNull), + EmptyObjectFacts = All & ~IsUndefinedOrNull, + UnknownFacts = All & ~IsUndefinedOrNull, AllTypeofNE = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined, - EmptyObjectFacts = All, // Masks OrFactsMask = TypeofEQFunction | TypeofNEObject, AndFactsMask = All & ~OrFactsMask, @@ -8887,7 +8892,7 @@ namespace ts { if (getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration))) { // In strict null checking mode, if a default value of a non-undefined type is specified, remove // undefined from the final type. - return strictNullChecks && !(getFalsyFlags(checkDeclarationInitializer(declaration, CheckMode.Normal)) & TypeFlags.Undefined) ? getNonUndefinedType(type) : type; + return strictNullChecks && !(getTypeFacts(checkDeclarationInitializer(declaration, CheckMode.Normal)) & TypeFacts.IsUndefined) ? getNonUndefinedType(type) : type; } return widenTypeInferredFromInitializer(declaration, getUnionType([getNonUndefinedType(type), checkDeclarationInitializer(declaration, CheckMode.Normal)], UnionReduction.Subtype)); } @@ -17996,7 +18001,7 @@ namespace ts { const sourceSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(sourceType)); const targetSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(targetType)); const callbacks = sourceSig && targetSig && !getTypePredicateOfSignature(sourceSig) && !getTypePredicateOfSignature(targetSig) && - (getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable); + (getTypeFacts(sourceType) & TypeFacts.IsUndefinedOrNull) === (getTypeFacts(targetType) & TypeFacts.IsUndefinedOrNull); let related = callbacks ? compareSignaturesRelated(targetSig, sourceSig, (checkMode & SignatureCheckMode.StrictArity) | (strictVariance ? SignatureCheckMode.StrictCallback : SignatureCheckMode.BivariantCallback), reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers) : !(checkMode & SignatureCheckMode.Callback) && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors); @@ -21364,31 +21369,8 @@ namespace ts { return value.base10Value === "0"; } - function getFalsyFlagsOfTypes(types: Type[]): TypeFlags { - let result: TypeFlags = 0; - for (const t of types) { - result |= getFalsyFlags(t); - } - return result; - } - - // Returns the String, Number, Boolean, StringLiteral, NumberLiteral, BooleanLiteral, Void, Undefined, or Null - // flags for the string, number, boolean, "", 0, false, void, undefined, or null types respectively. Returns - // no flags for all other types (including non-falsy literal types). - function getFalsyFlags(type: Type): TypeFlags { - const t = type.flags & TypeFlags.Intersection ? getBaseConstraintOrType(type) : type; - return t.flags & TypeFlags.Union ? getFalsyFlagsOfTypes((t as UnionType).types) : - t.flags & TypeFlags.StringLiteral ? (t as StringLiteralType).value === "" ? TypeFlags.StringLiteral : 0 : - t.flags & TypeFlags.NumberLiteral ? (t as NumberLiteralType).value === 0 ? TypeFlags.NumberLiteral : 0 : - t.flags & TypeFlags.BigIntLiteral ? isZeroBigInt(t as BigIntLiteralType) ? TypeFlags.BigIntLiteral : 0 : - t.flags & TypeFlags.BooleanLiteral ? (t === falseType || t === regularFalseType) ? TypeFlags.BooleanLiteral : 0 : - t.flags & TypeFlags.PossiblyFalsy; - } - function removeDefinitelyFalsyTypes(type: Type): Type { - return getFalsyFlags(type) & TypeFlags.DefinitelyFalsy ? - filterType(type, t => !(getFalsyFlags(t) & TypeFlags.DefinitelyFalsy)) : - type; + return filterType(type, t => !!(getTypeFacts(t) & TypeFacts.Truthy)); } function extractDefinitelyFalsyTypes(type: Type): Type { @@ -21436,14 +21418,7 @@ namespace ts { } function getNonNullableType(type: Type): Type { - if (strictNullChecks) { - // First reduce away any constituents that are assignable to 'undefined' or 'null'. This not only eliminates - // 'undefined' and 'null', but also higher-order types such as a type parameter 'U extends undefined | null' - // that isn't eliminated by a NonNullable instantiation. - const reducedType = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); - return maybeTypeOfKind(reducedType, TypeFlags.Instantiable) ? getGlobalNonNullableTypeInstantiation(reducedType) : reducedType; - } - return type; + return strictNullChecks ? getAdjustedTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type; } function addOptionalTypeMarker(type: Type) { @@ -23533,13 +23508,16 @@ namespace ts { resolved.members.get("bind" as __String) && isTypeSubtypeOf(type, globalFunctionType)); } - function getTypeFacts(type: Type, ignoreObjects = false): TypeFacts { + function getTypeFacts(type: Type): TypeFacts { + if (type.flags & (TypeFlags.Intersection | TypeFlags.Instantiable)) { + type = getBaseConstraintOfType(type) || unknownType; + } const flags = type.flags; - if (flags & TypeFlags.String) { + if (flags & (TypeFlags.String | TypeFlags.StringMapping)) { return strictNullChecks ? TypeFacts.StringStrictFacts : TypeFacts.StringFacts; } - if (flags & TypeFlags.StringLiteral) { - const isEmpty = (type as StringLiteralType).value === ""; + if (flags & (TypeFlags.StringLiteral | TypeFlags.TemplateLiteral)) { + const isEmpty = flags & TypeFlags.StringLiteral && (type as StringLiteralType).value === ""; return strictNullChecks ? isEmpty ? TypeFacts.EmptyStringStrictFacts : TypeFacts.NonEmptyStringStrictFacts : isEmpty ? TypeFacts.EmptyStringFacts : TypeFacts.NonEmptyStringFacts; @@ -23571,16 +23549,16 @@ namespace ts { (type === falseType || type === regularFalseType) ? TypeFacts.FalseFacts : TypeFacts.TrueFacts; } if (flags & TypeFlags.Object) { - if (ignoreObjects) { - return TypeFacts.AndFactsMask; // This is the identity element for computing type facts of intersection. - } return getObjectFlags(type) & ObjectFlags.Anonymous && isEmptyObjectType(type as ObjectType) ? strictNullChecks ? TypeFacts.EmptyObjectStrictFacts : TypeFacts.EmptyObjectFacts : isFunctionObjectType(type as ObjectType) ? strictNullChecks ? TypeFacts.FunctionStrictFacts : TypeFacts.FunctionFacts : strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts; } - if (flags & (TypeFlags.Void | TypeFlags.Undefined)) { + if (flags & TypeFlags.Void) { + return TypeFacts.VoidFacts; + } + if (flags & TypeFlags.Undefined) { return TypeFacts.UndefinedFacts; } if (flags & TypeFlags.Null) { @@ -23595,31 +23573,29 @@ namespace ts { if (flags & TypeFlags.Never) { return TypeFacts.None; } - if (flags & TypeFlags.Instantiable) { - return !isPatternLiteralType(type) ? getTypeFacts(getBaseConstraintOfType(type) || unknownType, ignoreObjects) : - strictNullChecks ? TypeFacts.NonEmptyStringStrictFacts : TypeFacts.NonEmptyStringFacts; - } if (flags & TypeFlags.Union) { - return reduceLeft((type as UnionType).types, (facts, t) => facts | getTypeFacts(t, ignoreObjects), TypeFacts.None); + return reduceLeft((type as UnionType).types, (facts, t) => facts | getTypeFacts(t), TypeFacts.None); } if (flags & TypeFlags.Intersection) { - // When an intersection contains a primitive type we ignore object type constituents as they are - // presumably type tags. For example, in string & { __kind__: "name" } we ignore the object type. - ignoreObjects ||= maybeTypeOfKind(type, TypeFlags.Primitive); - return getIntersectionTypeFacts(type as IntersectionType, ignoreObjects); + return getIntersectionTypeFacts(type as IntersectionType); } - return TypeFacts.All; + return TypeFacts.UnknownFacts; } - function getIntersectionTypeFacts(type: IntersectionType, ignoreObjects: boolean): TypeFacts { + function getIntersectionTypeFacts(type: IntersectionType): TypeFacts { + // When an intersection contains a primitive type we ignore object type constituents as they are + // presumably type tags. For example, in string & { __kind__: "name" } we ignore the object type. + const ignoreObjects = maybeTypeOfKind(type, TypeFlags.Primitive); // When computing the type facts of an intersection type, certain type facts are computed as `and` // and others are computed as `or`. let oredFacts = TypeFacts.None; let andedFacts = TypeFacts.All; for (const t of type.types) { - const f = getTypeFacts(t, ignoreObjects); - oredFacts |= f; - andedFacts &= f; + if (!(ignoreObjects && t.flags & TypeFlags.Object)) { + const f = getTypeFacts(t); + oredFacts |= f; + andedFacts &= f; + } } return oredFacts & TypeFacts.OrFactsMask | andedFacts & TypeFacts.AndFactsMask; } @@ -23628,7 +23604,10 @@ namespace ts { return filterType(type, t => (getTypeFacts(t) & include) !== 0); } - function getIntersectionWithFacts(type: Type, facts: TypeFacts) { + // This function is similar to getTypeWithFacts, except that in strictNullChecks mode it replaces type + // unknown with the union {} | null | undefined (and reduces that accordingly), and it intersects remaining + // instantiable types with {}, {} | null, or {} | undefined in order to remove null and/or undefined. + function getAdjustedTypeWithFacts(type: Type, facts: TypeFacts) { const reduced = recombineUnknownType(getTypeWithFacts(strictNullChecks && type.flags & TypeFlags.Unknown ? unknownUnionType : type, facts)); if (strictNullChecks) { switch (facts) { @@ -24887,10 +24866,10 @@ namespace ts { function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type { if (isMatchingReference(reference, expr)) { - return getIntersectionWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy); + return getAdjustedTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy); } if (strictNullChecks && assumeTrue && optionalChainContainsReference(expr, reference)) { - type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); + type = getAdjustedTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); } const access = getDiscriminantPropertyAccess(expr, type); if (access) { @@ -25036,7 +25015,7 @@ namespace ts { // Note that we include any and unknown in the exclusion test because their domain includes null and undefined. const removeNullable = equalsOperator !== assumeTrue && everyType(valueType, t => !!(t.flags & nullableFlags)) || equalsOperator === assumeTrue && everyType(valueType, t => !(t.flags & (TypeFlags.AnyOrUnknown | nullableFlags))); - return removeNullable ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type; + return removeNullable ? getAdjustedTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type; } function narrowTypeByEquality(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type { @@ -25066,7 +25045,7 @@ namespace ts { valueType.flags & TypeFlags.Null ? assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull : assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined; - return getIntersectionWithFacts(type, facts); + return getAdjustedTypeWithFacts(type, facts); } if (assumeTrue) { const filterFn: (t: Type) => boolean = operator === SyntaxKind.EqualsEqualsToken ? @@ -25088,7 +25067,7 @@ namespace ts { const target = getReferenceCandidate(typeOfExpr.expression); if (!isMatchingReference(reference, target)) { if (strictNullChecks && optionalChainContainsReference(target, reference) && assumeTrue === (literal.text !== "undefined")) { - return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); + return getAdjustedTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); } return type; } @@ -25308,7 +25287,7 @@ namespace ts { const left = getReferenceCandidate(expr.left); if (!isMatchingReference(reference, left)) { if (assumeTrue && strictNullChecks && optionalChainContainsReference(left, reference)) { - return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); + return getAdjustedTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); } return type; } @@ -25405,7 +25384,7 @@ namespace ts { } if (strictNullChecks && assumeTrue && optionalChainContainsReference(predicateArgument, reference) && !(getTypeFacts(predicate.type) & TypeFacts.EQUndefined)) { - type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); + type = getAdjustedTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); } const access = getDiscriminantPropertyAccess(predicateArgument, type); if (access) { @@ -25464,7 +25443,7 @@ namespace ts { function narrowTypeByOptionality(type: Type, expr: Expression, assumePresent: boolean): Type { if (isMatchingReference(reference, expr)) { - return getTypeWithFacts(type, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull); + return getAdjustedTypeWithFacts(type, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull); } const access = getDiscriminantPropertyAccess(expr, type); if (access) { @@ -25556,8 +25535,8 @@ namespace ts { const annotationIncludesUndefined = strictNullChecks && declaration.kind === SyntaxKind.Parameter && declaration.initializer && - getFalsyFlags(declaredType) & TypeFlags.Undefined && - !(getFalsyFlags(checkExpression(declaration.initializer)) & TypeFlags.Undefined); + getTypeFacts(declaredType) & TypeFacts.IsUndefined && + !(getTypeFacts(checkExpression(declaration.initializer)) & TypeFacts.IsUndefined); popTypeResolution(); return annotationIncludesUndefined ? getTypeWithFacts(declaredType, TypeFacts.NEUndefined) : declaredType; @@ -25914,7 +25893,7 @@ namespace ts { return convertAutoToAny(flowType); } } - else if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) { + else if (!assumeInitialized && !(getTypeFacts(type) & TypeFacts.IsUndefined) && getTypeFacts(flowType) & TypeFacts.IsUndefined) { error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); // Return the declared type to reduce follow-on errors return type; @@ -28891,23 +28870,23 @@ namespace ts { } function isNullableType(type: Type) { - return !!((strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable); + return !!(getTypeFacts(type) & TypeFacts.IsUndefinedOrNull); } function getNonNullableTypeIfNeeded(type: Type) { return isNullableType(type) ? getNonNullableType(type) : type; } - function reportObjectPossiblyNullOrUndefinedError(node: Node, flags: TypeFlags) { - error(node, flags & TypeFlags.Undefined ? flags & TypeFlags.Null ? + function reportObjectPossiblyNullOrUndefinedError(node: Node, facts: TypeFacts) { + error(node, facts & TypeFacts.IsUndefined ? facts & TypeFacts.IsNull ? Diagnostics.Object_is_possibly_null_or_undefined : Diagnostics.Object_is_possibly_undefined : Diagnostics.Object_is_possibly_null ); } - function reportCannotInvokePossiblyNullOrUndefinedError(node: Node, flags: TypeFlags) { - error(node, flags & TypeFlags.Undefined ? flags & TypeFlags.Null ? + function reportCannotInvokePossiblyNullOrUndefinedError(node: Node, facts: TypeFacts) { + error(node, facts & TypeFacts.IsUndefined ? facts & TypeFacts.IsNull ? Diagnostics.Cannot_invoke_an_object_which_is_possibly_null_or_undefined : Diagnostics.Cannot_invoke_an_object_which_is_possibly_undefined : Diagnostics.Cannot_invoke_an_object_which_is_possibly_null @@ -28917,15 +28896,15 @@ namespace ts { function checkNonNullTypeWithReporter( type: Type, node: Node, - reportError: (node: Node, kind: TypeFlags) => void + reportError: (node: Node, facts: TypeFacts) => void ): Type { if (strictNullChecks && type.flags & TypeFlags.Unknown) { error(node, Diagnostics.Object_is_of_type_unknown); return errorType; } - const kind = (strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable; - if (kind) { - reportError(node, kind); + const facts = getTypeFacts(type); + if (facts & TypeFacts.IsUndefinedOrNull) { + reportError(node, facts); const t = getNonNullableType(type); return t.flags & (TypeFlags.Nullable | TypeFlags.Never) ? errorType : t; } @@ -29271,7 +29250,7 @@ namespace ts { assumeUninitialized = true; } const flowType = getFlowTypeOfReference(node, propType, assumeUninitialized ? getOptionalType(propType) : propType); - if (assumeUninitialized && !(getFalsyFlags(propType) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) { + if (assumeUninitialized && !(getTypeFacts(propType) & TypeFacts.IsUndefined) && getTypeFacts(flowType) & TypeFacts.IsUndefined) { error(errorNode, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(prop!)); // TODO: GH#18217 // Return the declared type to reduce follow-on errors return propType; @@ -33258,7 +33237,7 @@ namespace ts { const type = getTypeOfSymbol(symbol); if (strictNullChecks && !(type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.Never)) && - !(exactOptionalPropertyTypes ? symbol.flags & SymbolFlags.Optional : getFalsyFlags(type) & TypeFlags.Undefined)) { + !(exactOptionalPropertyTypes ? symbol.flags & SymbolFlags.Optional : getTypeFacts(type) & TypeFacts.IsUndefined)) { error(expr, Diagnostics.The_operand_of_a_delete_operator_must_be_optional); } } @@ -33695,7 +33674,7 @@ namespace ts { // In strict null checking mode, if a default value of a non-undefined type is specified, remove // undefined from the final type. if (strictNullChecks && - !(getFalsyFlags(checkExpression(prop.objectAssignmentInitializer)) & TypeFlags.Undefined)) { + !(getTypeFacts(checkExpression(prop.objectAssignmentInitializer)) & TypeFacts.IsUndefined)) { sourceType = getTypeWithFacts(sourceType, TypeFacts.NEUndefined); } checkBinaryLikeExpression(prop.name, prop.equalsToken!, prop.objectAssignmentInitializer, checkMode); @@ -34154,7 +34133,7 @@ namespace ts { case SyntaxKind.BarBarToken: case SyntaxKind.BarBarEqualsToken: { const resultType = getTypeFacts(leftType) & TypeFacts.Falsy ? - getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], UnionReduction.Subtype) : + getUnionType([getNonNullableType(removeDefinitelyFalsyTypes(leftType)), rightType], UnionReduction.Subtype) : leftType; if (operator === SyntaxKind.BarBarEqualsToken) { checkAssignmentOperator(rightType); @@ -38109,7 +38088,7 @@ namespace ts { if (isModuleExportsAccessExpression(location)) return; const type = checkTruthinessExpression(location); const isPropertyExpressionCast = isPropertyAccessExpression(location) && isTypeAssertion(location.expression); - if (getFalsyFlags(type) || isPropertyExpressionCast) return; + if (!(getTypeFacts(type) & TypeFacts.Truthy) || isPropertyExpressionCast) return; // While it technically should be invalid for any known-truthy value // to be tested, we de-scope to functions and Promises unreferenced in @@ -40237,7 +40216,7 @@ namespace ts { const propName = (member as PropertyDeclaration).name; if (isIdentifier(propName) || isPrivateIdentifier(propName) || isComputedPropertyName(propName)) { const type = getTypeOfSymbol(getSymbolOfNode(member)); - if (!(type.flags & TypeFlags.AnyOrUnknown || getFalsyFlags(type) & TypeFlags.Undefined)) { + if (!(type.flags & TypeFlags.AnyOrUnknown || getTypeFacts(type) & TypeFacts.IsUndefined)) { if (!constructor || !isPropertyInitializedInConstructor(propName, type, constructor)) { error(member.name, Diagnostics.Property_0_has_no_initializer_and_is_not_definitely_assigned_in_the_constructor, declarationNameToString(propName)); } @@ -40263,7 +40242,7 @@ namespace ts { setParent(reference, staticBlock); reference.flowNode = staticBlock.returnFlowNode; const flowType = getFlowTypeOfReference(reference, propType, getOptionalType(propType)); - if (!(getFalsyFlags(flowType) & TypeFlags.Undefined)) { + if (!(getTypeFacts(flowType) & TypeFacts.IsUndefined)) { return true; } } @@ -40279,7 +40258,7 @@ namespace ts { setParent(reference, constructor); reference.flowNode = constructor.returnFlowNode; const flowType = getFlowTypeOfReference(reference, propType, getOptionalType(propType)); - return !(getFalsyFlags(flowType) & TypeFlags.Undefined); + return !(getTypeFacts(flowType) & TypeFacts.IsUndefined); } diff --git a/tests/baselines/reference/discriminatedUnionJsxElement.types b/tests/baselines/reference/discriminatedUnionJsxElement.types index 4f7b5a1ccdd9c..9057c9c55ab3e 100644 --- a/tests/baselines/reference/discriminatedUnionJsxElement.types +++ b/tests/baselines/reference/discriminatedUnionJsxElement.types @@ -14,8 +14,8 @@ function Menu >data : IData const listItemVariant = data.menuItemsVariant ?? ListItemVariant.OneLine; ->listItemVariant : ListItemVariant.OneLine | NonNullable ->data.menuItemsVariant ?? ListItemVariant.OneLine : ListItemVariant.OneLine | NonNullable +>listItemVariant : ListItemVariant.OneLine | MenuItemVariant +>data.menuItemsVariant ?? ListItemVariant.OneLine : ListItemVariant.OneLine | MenuItemVariant >data.menuItemsVariant : MenuItemVariant | undefined >data : IData >menuItemsVariant : MenuItemVariant | undefined diff --git a/tests/baselines/reference/mappedTypes4.types b/tests/baselines/reference/mappedTypes4.types index 434fcf1f7e87f..f96bf1c5e8f48 100644 --- a/tests/baselines/reference/mappedTypes4.types +++ b/tests/baselines/reference/mappedTypes4.types @@ -30,14 +30,14 @@ function boxify(obj: T): Boxified { >obj : (T & null) | (T & object) result[k] = { value: obj[k] }; ->result[k] = { value: obj[k] } : { value: NonNullable[Extract]; } +>result[k] = { value: obj[k] } : { value: (T & object)[Extract]; } >result[k] : Boxified[Extract] >result : Boxified >k : Extract ->{ value: obj[k] } : { value: NonNullable[Extract]; } ->value : NonNullable[Extract] ->obj[k] : NonNullable[Extract] ->obj : NonNullable +>{ value: obj[k] } : { value: (T & object)[Extract]; } +>value : (T & object)[Extract] +>obj[k] : (T & object)[Extract] +>obj : T & object >k : Extract } return result; diff --git a/tests/baselines/reference/neverType.js b/tests/baselines/reference/neverType.js index eb7ee33148013..9d81d49d76a8b 100644 --- a/tests/baselines/reference/neverType.js +++ b/tests/baselines/reference/neverType.js @@ -180,7 +180,7 @@ declare function infiniteLoop1(): void; declare function infiniteLoop2(): never; declare function move1(direction: "up" | "down"): 1 | -1; declare function move2(direction: "up" | "down"): 1 | -1; -declare function check(x: T | undefined): T; +declare function check(x: T | undefined): NonNullable; declare class C { void1(): void; void2(): void; diff --git a/tests/baselines/reference/neverType.types b/tests/baselines/reference/neverType.types index 7ae38d11f76ce..b7e08eb503ad6 100644 --- a/tests/baselines/reference/neverType.types +++ b/tests/baselines/reference/neverType.types @@ -112,11 +112,11 @@ function move2(direction: "up" | "down") { } function check(x: T | undefined) { ->check : (x: T | undefined) => T +>check : (x: T | undefined) => NonNullable >x : T | undefined return x || error("Undefined value"); ->x || error("Undefined value") : T +>x || error("Undefined value") : NonNullable >x : T | undefined >error("Undefined value") : never >error : (message: string) => never diff --git a/tests/baselines/reference/nonNullParameterExtendingStringAssignableToString.types b/tests/baselines/reference/nonNullParameterExtendingStringAssignableToString.types index 5f9783c5f902f..64c099cfa4e8d 100644 --- a/tests/baselines/reference/nonNullParameterExtendingStringAssignableToString.types +++ b/tests/baselines/reference/nonNullParameterExtendingStringAssignableToString.types @@ -25,7 +25,7 @@ function fn(one: T, two: U) { foo(two!); >foo(two!) : void >foo : (p: string) => void ->two! : NonNullable +>two! : U >two : U foo(three!); // this line is the important one diff --git a/tests/baselines/reference/nonNullableTypes1.js b/tests/baselines/reference/nonNullableTypes1.js new file mode 100644 index 0000000000000..51d4918aa60a1 --- /dev/null +++ b/tests/baselines/reference/nonNullableTypes1.js @@ -0,0 +1,85 @@ +//// [nonNullableTypes1.ts] +function f1(x: T) { + let y = x || "hello"; // NonNullable | string +} + +function error(): never { + throw new Error(); +} + +function f2(x: T) { // NonNullable + return x || error(); +} + +function f3(x: unknown) { + let y = x!; // {} +} + +function f4(obj: T) { + if (obj?.x === "hello") { + obj; // NonNullable + } + if (obj?.x) { + obj; // NonNullable + } + if (typeof obj?.x === "string") { + obj; // NonNullable + } +} + +class A { + x = "hello"; + foo() { + let zz = this?.x; // string + } +} + + +//// [nonNullableTypes1.js] +"use strict"; +function f1(x) { + var y = x || "hello"; // NonNullable | string +} +function error() { + throw new Error(); +} +function f2(x) { + return x || error(); +} +function f3(x) { + var y = x; // {} +} +function f4(obj) { + if ((obj === null || obj === void 0 ? void 0 : obj.x) === "hello") { + obj; // NonNullable + } + if (obj === null || obj === void 0 ? void 0 : obj.x) { + obj; // NonNullable + } + if (typeof (obj === null || obj === void 0 ? void 0 : obj.x) === "string") { + obj; // NonNullable + } +} +var A = /** @class */ (function () { + function A() { + this.x = "hello"; + } + A.prototype.foo = function () { + var zz = this === null || this === void 0 ? void 0 : this.x; // string + }; + return A; +}()); + + +//// [nonNullableTypes1.d.ts] +declare function f1(x: T): void; +declare function error(): never; +declare function f2(x: T): NonNullable; +declare function f3(x: unknown): void; +declare function f4(obj: T): void; +declare class A { + x: string; + foo(): void; +} diff --git a/tests/baselines/reference/nonNullableTypes1.symbols b/tests/baselines/reference/nonNullableTypes1.symbols new file mode 100644 index 0000000000000..7737d22f68027 --- /dev/null +++ b/tests/baselines/reference/nonNullableTypes1.symbols @@ -0,0 +1,89 @@ +=== tests/cases/compiler/nonNullableTypes1.ts === +function f1(x: T) { +>f1 : Symbol(f1, Decl(nonNullableTypes1.ts, 0, 0)) +>T : Symbol(T, Decl(nonNullableTypes1.ts, 0, 12)) +>x : Symbol(x, Decl(nonNullableTypes1.ts, 0, 15)) +>T : Symbol(T, Decl(nonNullableTypes1.ts, 0, 12)) + + let y = x || "hello"; // NonNullable | string +>y : Symbol(y, Decl(nonNullableTypes1.ts, 1, 7)) +>x : Symbol(x, Decl(nonNullableTypes1.ts, 0, 15)) +} + +function error(): never { +>error : Symbol(error, Decl(nonNullableTypes1.ts, 2, 1)) + + throw new Error(); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +} + +function f2(x: T) { // NonNullable +>f2 : Symbol(f2, Decl(nonNullableTypes1.ts, 6, 1)) +>T : Symbol(T, Decl(nonNullableTypes1.ts, 8, 12)) +>x : Symbol(x, Decl(nonNullableTypes1.ts, 8, 15)) +>T : Symbol(T, Decl(nonNullableTypes1.ts, 8, 12)) + + return x || error(); +>x : Symbol(x, Decl(nonNullableTypes1.ts, 8, 15)) +>error : Symbol(error, Decl(nonNullableTypes1.ts, 2, 1)) +} + +function f3(x: unknown) { +>f3 : Symbol(f3, Decl(nonNullableTypes1.ts, 10, 1)) +>x : Symbol(x, Decl(nonNullableTypes1.ts, 12, 12)) + + let y = x!; // {} +>y : Symbol(y, Decl(nonNullableTypes1.ts, 13, 7)) +>x : Symbol(x, Decl(nonNullableTypes1.ts, 12, 12)) +} + +function f4(obj: T) { +>f4 : Symbol(f4, Decl(nonNullableTypes1.ts, 14, 1)) +>T : Symbol(T, Decl(nonNullableTypes1.ts, 16, 12)) +>x : Symbol(x, Decl(nonNullableTypes1.ts, 16, 23)) +>obj : Symbol(obj, Decl(nonNullableTypes1.ts, 16, 49)) +>T : Symbol(T, Decl(nonNullableTypes1.ts, 16, 12)) + + if (obj?.x === "hello") { +>obj?.x : Symbol(x, Decl(nonNullableTypes1.ts, 16, 23)) +>obj : Symbol(obj, Decl(nonNullableTypes1.ts, 16, 49)) +>x : Symbol(x, Decl(nonNullableTypes1.ts, 16, 23)) + + obj; // NonNullable +>obj : Symbol(obj, Decl(nonNullableTypes1.ts, 16, 49)) + } + if (obj?.x) { +>obj?.x : Symbol(x, Decl(nonNullableTypes1.ts, 16, 23)) +>obj : Symbol(obj, Decl(nonNullableTypes1.ts, 16, 49)) +>x : Symbol(x, Decl(nonNullableTypes1.ts, 16, 23)) + + obj; // NonNullable +>obj : Symbol(obj, Decl(nonNullableTypes1.ts, 16, 49)) + } + if (typeof obj?.x === "string") { +>obj?.x : Symbol(x, Decl(nonNullableTypes1.ts, 16, 23)) +>obj : Symbol(obj, Decl(nonNullableTypes1.ts, 16, 49)) +>x : Symbol(x, Decl(nonNullableTypes1.ts, 16, 23)) + + obj; // NonNullable +>obj : Symbol(obj, Decl(nonNullableTypes1.ts, 16, 49)) + } +} + +class A { +>A : Symbol(A, Decl(nonNullableTypes1.ts, 26, 1)) + + x = "hello"; +>x : Symbol(A.x, Decl(nonNullableTypes1.ts, 28, 9)) + + foo() { +>foo : Symbol(A.foo, Decl(nonNullableTypes1.ts, 29, 16)) + + let zz = this?.x; // string +>zz : Symbol(zz, Decl(nonNullableTypes1.ts, 31, 11)) +>this?.x : Symbol(A.x, Decl(nonNullableTypes1.ts, 28, 9)) +>this : Symbol(A, Decl(nonNullableTypes1.ts, 26, 1)) +>x : Symbol(A.x, Decl(nonNullableTypes1.ts, 28, 9)) + } +} + diff --git a/tests/baselines/reference/nonNullableTypes1.types b/tests/baselines/reference/nonNullableTypes1.types new file mode 100644 index 0000000000000..3d319114e794c --- /dev/null +++ b/tests/baselines/reference/nonNullableTypes1.types @@ -0,0 +1,95 @@ +=== tests/cases/compiler/nonNullableTypes1.ts === +function f1(x: T) { +>f1 : (x: T) => void +>x : T + + let y = x || "hello"; // NonNullable | string +>y : string | NonNullable +>x || "hello" : "hello" | NonNullable +>x : T +>"hello" : "hello" +} + +function error(): never { +>error : () => never + + throw new Error(); +>new Error() : Error +>Error : ErrorConstructor +} + +function f2(x: T) { // NonNullable +>f2 : (x: T) => NonNullable +>x : T + + return x || error(); +>x || error() : NonNullable +>x : T +>error() : never +>error : () => never +} + +function f3(x: unknown) { +>f3 : (x: unknown) => void +>x : unknown + + let y = x!; // {} +>y : {} +>x! : {} +>x : unknown +} + +function f4(obj: T) { +>f4 : (obj: T) => void +>x : string +>obj : T + + if (obj?.x === "hello") { +>obj?.x === "hello" : boolean +>obj?.x : string | undefined +>obj : { x: string; } | undefined +>x : string | undefined +>"hello" : "hello" + + obj; // NonNullable +>obj : NonNullable + } + if (obj?.x) { +>obj?.x : string | undefined +>obj : { x: string; } | undefined +>x : string | undefined + + obj; // NonNullable +>obj : NonNullable + } + if (typeof obj?.x === "string") { +>typeof obj?.x === "string" : boolean +>typeof obj?.x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>obj?.x : string | undefined +>obj : { x: string; } | undefined +>x : string | undefined +>"string" : "string" + + obj; // NonNullable +>obj : NonNullable + } +} + +class A { +>A : A + + x = "hello"; +>x : string +>"hello" : "hello" + + foo() { +>foo : () => void + + let zz = this?.x; // string +>zz : string +>this?.x : string +>this : this +>x : string + } +} + diff --git a/tests/baselines/reference/nullishCoalescingOperator2.types b/tests/baselines/reference/nullishCoalescingOperator2.types index dd1e31c4038c4..085b3de8d5987 100644 --- a/tests/baselines/reference/nullishCoalescingOperator2.types +++ b/tests/baselines/reference/nullishCoalescingOperator2.types @@ -75,8 +75,8 @@ const aa6 = a6 ?? 'whatever' >'whatever' : "whatever" const aa7 = a7 ?? 'whatever' ->aa7 : unknown ->a7 ?? 'whatever' : unknown +>aa7 : {} +>a7 ?? 'whatever' : {} >a7 : unknown >'whatever' : "whatever" diff --git a/tests/baselines/reference/nullishCoalescingOperator_es2020.types b/tests/baselines/reference/nullishCoalescingOperator_es2020.types index 2dead6366b7a6..aec3adb3d462a 100644 --- a/tests/baselines/reference/nullishCoalescingOperator_es2020.types +++ b/tests/baselines/reference/nullishCoalescingOperator_es2020.types @@ -75,8 +75,8 @@ const aa6 = a6 ?? 'whatever' >'whatever' : "whatever" const aa7 = a7 ?? 'whatever' ->aa7 : unknown ->a7 ?? 'whatever' : unknown +>aa7 : {} +>a7 ?? 'whatever' : {} >a7 : unknown >'whatever' : "whatever" diff --git a/tests/baselines/reference/privateIdentifierChain.1.types b/tests/baselines/reference/privateIdentifierChain.1.types index faf9a763c04f3..34deba67625a5 100644 --- a/tests/baselines/reference/privateIdentifierChain.1.types +++ b/tests/baselines/reference/privateIdentifierChain.1.types @@ -28,10 +28,10 @@ class A { this?.getA().#b; // Error >this?.getA().#b : A | undefined ->this?.getA() : A | undefined ->this?.getA : (() => A) | undefined +>this?.getA() : A +>this?.getA : () => A >this : this ->getA : (() => A) | undefined +>getA : () => A } } diff --git a/tests/baselines/reference/simplifyingConditionalWithInteriorConditionalIsRelated.types b/tests/baselines/reference/simplifyingConditionalWithInteriorConditionalIsRelated.types index 52e8ef8f14fbe..d9b814a227490 100644 --- a/tests/baselines/reference/simplifyingConditionalWithInteriorConditionalIsRelated.types +++ b/tests/baselines/reference/simplifyingConditionalWithInteriorConditionalIsRelated.types @@ -15,7 +15,7 @@ function JustConditional(): ConditionalType { >JustConditional : () => ConditionalType return ConditionalOrUndefined()!; // shouldn't error ->ConditionalOrUndefined()! : NonNullable> +>ConditionalOrUndefined()! : ConditionalType >ConditionalOrUndefined() : ConditionalType | undefined >ConditionalOrUndefined : () => ConditionalType | undefined } diff --git a/tests/cases/compiler/nonNullableTypes1.ts b/tests/cases/compiler/nonNullableTypes1.ts new file mode 100644 index 0000000000000..7006c3c1fc698 --- /dev/null +++ b/tests/cases/compiler/nonNullableTypes1.ts @@ -0,0 +1,37 @@ +// @strict: true +// @declaration: true + +function f1(x: T) { + let y = x || "hello"; // NonNullable | string +} + +function error(): never { + throw new Error(); +} + +function f2(x: T) { // NonNullable + return x || error(); +} + +function f3(x: unknown) { + let y = x!; // {} +} + +function f4(obj: T) { + if (obj?.x === "hello") { + obj; // NonNullable + } + if (obj?.x) { + obj; // NonNullable + } + if (typeof obj?.x === "string") { + obj; // NonNullable + } +} + +class A { + x = "hello"; + foo() { + let zz = this?.x; // string + } +}