diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 322db076879..cadf4edcdcd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -354,6 +354,7 @@ namespace ts { const autoType = createIntrinsicType(TypeFlags.Any, "any"); const wildcardType = createIntrinsicType(TypeFlags.Any, "any"); const errorType = createIntrinsicType(TypeFlags.Any, "error"); + const unknownType = createIntrinsicType(TypeFlags.Unknown, "unknown"); const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined"); const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined"); const nullType = createIntrinsicType(TypeFlags.Null, "null"); @@ -3099,6 +3100,9 @@ namespace ts { if (type.flags & TypeFlags.Any) { return createKeywordTypeNode(SyntaxKind.AnyKeyword); } + if (type.flags & TypeFlags.Unknown) { + return createKeywordTypeNode(SyntaxKind.UnknownKeyword); + } if (type.flags & TypeFlags.String) { return createKeywordTypeNode(SyntaxKind.StringKeyword); } @@ -5720,6 +5724,7 @@ namespace ts { function isThislessType(node: TypeNode): boolean { switch (node.kind) { case SyntaxKind.AnyKeyword: + case SyntaxKind.UnknownKeyword: case SyntaxKind.StringKeyword: case SyntaxKind.NumberKeyword: case SyntaxKind.BooleanKeyword: @@ -8293,7 +8298,7 @@ namespace ts { // easier to reason about their origin. if (!(flags & TypeFlags.Never || flags & TypeFlags.Intersection && isEmptyIntersectionType(type))) { includes |= flags & ~TypeFlags.ConstructionFlags; - if (flags & TypeFlags.Any) { + if (flags & TypeFlags.AnyOrUnknown) { if (type === wildcardType) includes |= TypeFlags.Wildcard; } else if (!strictNullChecks && flags & TypeFlags.Nullable) { @@ -8404,8 +8409,8 @@ namespace ts { } const typeSet: Type[] = []; const includes = addTypesToUnion(typeSet, 0, types); - if (includes & TypeFlags.Any) { - return includes & TypeFlags.Wildcard ? wildcardType : anyType; + if (includes & TypeFlags.AnyOrUnknown) { + return includes & TypeFlags.Any ? includes & TypeFlags.Wildcard ? wildcardType : anyType : unknownType; } switch (unionReduction) { case UnionReduction.Literal: @@ -8508,7 +8513,7 @@ namespace ts { } else { includes |= flags & ~TypeFlags.ConstructionFlags; - if (flags & TypeFlags.Any) { + if (flags & TypeFlags.AnyOrUnknown) { if (type === wildcardType) includes |= TypeFlags.Wildcard; } else if ((strictNullChecks || !(flags & TypeFlags.Nullable)) && !contains(typeSet, type) && @@ -8579,9 +8584,6 @@ namespace ts { // Also, unlike union types, the order of the constituent types is preserved in order that overload resolution // for intersections of types with signatures can be deterministic. function getIntersectionType(types: Type[], aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { - if (types.length === 0) { - return emptyObjectType; - } const typeSet: Type[] = []; const includes = addTypesToIntersection(typeSet, 0, types); if (includes & TypeFlags.Never) { @@ -8598,6 +8600,9 @@ namespace ts { if (includes & TypeFlags.EmptyObject && !(includes & TypeFlags.Object)) { typeSet.push(emptyObjectType); } + if (typeSet.length === 0) { + return unknownType; + } if (typeSet.length === 1) { return typeSet[0]; } @@ -8893,7 +8898,7 @@ namespace ts { // an expression. This is to preserve backwards compatibility. For example, an element access 'this["foo"]' // has always been resolved eagerly using the constraint type of 'this' at the given location. if (isGenericIndexType(indexType) || !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) && isGenericObjectType(objectType)) { - if (objectType.flags & TypeFlags.Any) { + if (objectType.flags & TypeFlags.AnyOrUnknown) { return objectType; } // Defer the operation by creating an indexed access type. @@ -8961,6 +8966,9 @@ namespace ts { if (checkType === wildcardType || extendsType === wildcardType) { return wildcardType; } + if (extendsType.flags & TypeFlags.AnyOrUnknown) { + return instantiateType(root.trueType, mapper); + } // If this is a distributive conditional type and the check type is generic we need to defer // resolution of the conditional type such that a later instantiation will properly distribute // over union types. @@ -9195,6 +9203,9 @@ namespace ts { if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) { return anyType; } + if (left.flags & TypeFlags.Unknown || right.flags & TypeFlags.Unknown) { + return unknownType; + } if (left.flags & TypeFlags.Never) { return right; } @@ -9388,6 +9399,8 @@ namespace ts { case SyntaxKind.JSDocAllType: case SyntaxKind.JSDocUnknownType: return anyType; + case SyntaxKind.UnknownKeyword: + return unknownType; case SyntaxKind.StringKeyword: return stringType; case SyntaxKind.NumberKeyword: @@ -9756,7 +9769,7 @@ namespace ts { } function isMappableType(type: Type) { - return type.flags & (TypeFlags.Any | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection); + return type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection); } function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType { @@ -9855,7 +9868,7 @@ namespace ts { } function getWildcardInstantiation(type: Type) { - return type.flags & (TypeFlags.Primitive | TypeFlags.Any | TypeFlags.Never) ? type : + return type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never) ? type : type.wildcardInstantiation || (type.wildcardInstantiation = instantiateType(type, wildcardMapper)); } @@ -10269,7 +10282,7 @@ namespace ts { function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { const s = source.flags; const t = target.flags; - if (t & TypeFlags.Any || s & TypeFlags.Never || source === wildcardType) return true; + if (t & TypeFlags.AnyOrUnknown || s & TypeFlags.Never || source === wildcardType) return true; if (t & TypeFlags.Never) return false; if (s & TypeFlags.StringLike && t & TypeFlags.String) return true; if (s & TypeFlags.StringLiteral && s & TypeFlags.EnumLiteral && @@ -10995,7 +11008,7 @@ namespace ts { } } const constraint = getConstraintForRelation(source); - if (!constraint || (source.flags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) { + if (!constraint || (source.flags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.AnyOrUnknown)) { // A type variable with no constraint is not related to the non-primitive object type. if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) { errorInfo = saveErrorInfo; @@ -11438,7 +11451,7 @@ namespace ts { return indexTypesIdenticalTo(source, target, kind); } const targetInfo = getIndexInfoOfType(target, kind); - if (!targetInfo || targetInfo.type.flags & TypeFlags.Any && !sourceIsPrimitive) { + if (!targetInfo || targetInfo.type.flags & TypeFlags.AnyOrUnknown && !sourceIsPrimitive) { // Index signature of type any permits assignment from everything but primitives return Ternary.True; } @@ -14467,7 +14480,7 @@ namespace ts { // the entire control flow graph from the variable's declaration (i.e. when the flow container and // declaration container are the same). const assumeInitialized = isParameter || isAlias || isOuterVariable || isSpreadDestructuringAsignmentTarget || - type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || + type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & TypeFlags.AnyOrUnknown) !== 0 || isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) || node.parent.kind === SyntaxKind.NonNullExpression || declaration.kind === SyntaxKind.VariableDeclaration && (declaration).exclamationToken || @@ -16071,7 +16084,7 @@ namespace ts { } function isValidSpreadType(type: Type): boolean { - return !!(type.flags & (TypeFlags.Any | TypeFlags.NonPrimitive) || + return !!(type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.NonPrimitive) || getFalsyFlags(type) & TypeFlags.DefinitelyFalsy && isValidSpreadType(removeDefinitelyFalsyTypes(type)) || type.flags & TypeFlags.Object && !isGenericMappedType(type) || type.flags & TypeFlags.UnionOrIntersection && !forEach((type).types, t => !isValidSpreadType(t))); @@ -19624,7 +19637,7 @@ namespace ts { } // Functions with with an explicitly specified 'void' or 'any' return type don't need any return expressions. - if (returnType && maybeTypeOfKind(returnType, TypeFlags.Any | TypeFlags.Void)) { + if (returnType && maybeTypeOfKind(returnType, TypeFlags.AnyOrUnknown | TypeFlags.Void)) { return; } @@ -19952,7 +19965,7 @@ namespace ts { if (source.flags & kind) { return true; } - if (strict && source.flags & (TypeFlags.Any | TypeFlags.Void | TypeFlags.Undefined | TypeFlags.Null)) { + if (strict && source.flags & (TypeFlags.AnyOrUnknown | TypeFlags.Void | TypeFlags.Undefined | TypeFlags.Null)) { return false; } return !!(kind & TypeFlags.NumberLike) && isTypeAssignableTo(source, numberType) || @@ -23849,7 +23862,7 @@ namespace ts { const unwrappedReturnType = (getFunctionFlags(func) & FunctionFlags.AsyncGenerator) === FunctionFlags.Async ? getPromisedTypeOfPromise(returnType) // Async function : returnType; // AsyncGenerator function, Generator function, or normal function - return !!unwrappedReturnType && maybeTypeOfKind(unwrappedReturnType, TypeFlags.Void | TypeFlags.Any); + return !!unwrappedReturnType && maybeTypeOfKind(unwrappedReturnType, TypeFlags.Void | TypeFlags.AnyOrUnknown); } function checkReturnStatement(node: ReturnStatement) { @@ -24143,6 +24156,7 @@ namespace ts { // The predefined type keywords are reserved and cannot be used as names of user defined types. switch (name.escapedText) { case "any": + case "unknown": case "number": case "boolean": case "string": @@ -24563,7 +24577,7 @@ namespace ts { const propName = (member).name; if (isIdentifier(propName)) { const type = getTypeOfSymbol(getSymbolOfNode(member)); - if (!(type.flags & TypeFlags.Any || getFalsyFlags(type) & TypeFlags.Undefined)) { + if (!(type.flags & TypeFlags.AnyOrUnknown || getFalsyFlags(type) & TypeFlags.Undefined)) { 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)); } @@ -26735,7 +26749,7 @@ namespace ts { if (type === errorType) { return TypeReferenceSerializationKind.Unknown; } - else if (type.flags & TypeFlags.Any) { + else if (type.flags & TypeFlags.AnyOrUnknown) { return TypeReferenceSerializationKind.ObjectType; } else if (isTypeAssignableToKind(type, TypeFlags.Void | TypeFlags.Nullable | TypeFlags.Never)) {