Implement 'unknown' type in checker

This commit is contained in:
Anders Hejlsberg 2018-05-26 09:53:07 -07:00
parent d225065cf4
commit 2003b2a3e2

View file

@ -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(<IntersectionType>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<RelationComparisonResult>, 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(<TypeParameter>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 && (<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((<UnionOrIntersectionType>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 = (<PropertyDeclaration>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)) {