diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b8f078ea9a5..66e49ffff13 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2844,7 +2844,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 && declaration.initializer && !(getNullableKind(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) { + if (strictNullChecks && declaration.initializer && !(getCombinedTypeFlags(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) { type = getTypeWithFacts(type, TypeFacts.NEUndefined); } return type; @@ -2887,7 +2887,7 @@ namespace ts { } function addOptionality(type: Type, optional: boolean): Type { - return strictNullChecks && optional ? addNullableKind(type, TypeFlags.Undefined) : type; + return strictNullChecks && optional ? addTypeKind(type, TypeFlags.Undefined) : type; } // Return the inferred type for a variable, parameter, or property declaration @@ -3222,7 +3222,7 @@ namespace ts { if (!links.type) { const type = createObjectType(TypeFlags.Anonymous, symbol); links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ? - addNullableKind(type, TypeFlags.Undefined) : type; + addTypeKind(type, TypeFlags.Undefined) : type; } return links.type; } @@ -6746,7 +6746,7 @@ namespace ts { return getUnionType(types); } const supertype = forEach(primaryTypes, t => isSupertypeOfEach(t, primaryTypes) ? t : undefined); - return supertype && addNullableKind(supertype, getCombinedFlagsOfTypes(types) & TypeFlags.Nullable); + return supertype && addTypeKind(supertype, getCombinedFlagsOfTypes(types) & TypeFlags.Nullable); } function reportNoCommonSupertypeError(types: Type[], errorLocation: Node, errorMessageChainHead: DiagnosticMessageChain): void { @@ -6817,28 +6817,22 @@ namespace ts { return !!(type.flags & TypeFlags.Tuple); } - function getNullableKind(type: Type): TypeFlags { - let flags = type.flags; - if (flags & TypeFlags.Union) { - for (const t of (type as UnionType).types) { - flags |= t.flags; - } - } - return flags & TypeFlags.Nullable; + function getCombinedTypeFlags(type: Type): TypeFlags { + return type.flags & TypeFlags.Union ? getCombinedFlagsOfTypes((type).types) : type.flags; } - function addNullableKind(type: Type, kind: TypeFlags): Type { - if ((getNullableKind(type) & kind) !== kind) { - const types = [type]; - if (kind & TypeFlags.Undefined) { - types.push(undefinedType); - } - if (kind & TypeFlags.Null) { - types.push(nullType); - } - type = getUnionType(types); + function addTypeKind(type: Type, kind: TypeFlags) { + if ((getCombinedTypeFlags(type) & kind) === kind) { + return type; } - return type; + const types = [type]; + if (kind & TypeFlags.String) types.push(stringType); + if (kind & TypeFlags.Number) types.push(numberType); + if (kind & TypeFlags.Boolean) types.push(booleanType); + if (kind & TypeFlags.Void) types.push(voidType); + if (kind & TypeFlags.Undefined) types.push(undefinedType); + if (kind & TypeFlags.Null) types.push(nullType); + return getUnionType(types); } function getNonNullableType(type: Type): Type { @@ -7667,7 +7661,7 @@ namespace ts { if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) { return declaredType; } - const initialType = assumeInitialized ? declaredType : addNullableKind(declaredType, TypeFlags.Undefined); + const initialType = assumeInitialized ? declaredType : addTypeKind(declaredType, TypeFlags.Undefined); const visitedFlowStart = visitedFlowCount; const result = getTypeAtFlowNode(reference.flowNode); visitedFlowCount = visitedFlowStart; @@ -8163,7 +8157,7 @@ namespace ts { getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || !isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions); const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions); - if (!assumeInitialized && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) { + if (!assumeInitialized && !(getCombinedTypeFlags(type) & TypeFlags.Undefined) && getCombinedTypeFlags(flowType) & TypeFlags.Undefined) { error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); // Return the declared type to reduce follow-on errors return type; @@ -9945,7 +9939,7 @@ namespace ts { function checkNonNullExpression(node: Expression | QualifiedName) { const type = checkExpression(node); if (strictNullChecks) { - const kind = getNullableKind(type); + const kind = getCombinedTypeFlags(type) & TypeFlags.Nullable; if (kind) { error(node, kind & TypeFlags.Undefined ? kind & TypeFlags.Null ? Diagnostics.Object_is_possibly_null_or_undefined : @@ -11485,7 +11479,7 @@ namespace ts { if (strictNullChecks) { const declaration = symbol.valueDeclaration; if (declaration && (declaration).initializer) { - return addNullableKind(type, TypeFlags.Undefined); + return addTypeKind(type, TypeFlags.Undefined); } } return type; @@ -12411,7 +12405,7 @@ namespace ts { case SyntaxKind.InKeyword: return checkInExpression(left, right, leftType, rightType); case SyntaxKind.AmpersandAmpersandToken: - return strictNullChecks ? addNullableKind(rightType, getNullableKind(leftType)) : rightType; + return strictNullChecks ? addTypeKind(rightType, getCombinedTypeFlags(leftType) & TypeFlags.Falsy) : rightType; case SyntaxKind.BarBarToken: return getUnionType([getNonNullableType(leftType), rightType]); case SyntaxKind.EqualsToken: diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4c0e7a036db..3e8c2378b4c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2209,6 +2209,7 @@ namespace ts { /* @internal */ Nullable = Undefined | Null, + Falsy = String | Number | Boolean | Void | Undefined | Null, /* @internal */ Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null | Never, /* @internal */