Improved subtype compatibility for types of object literals

This commit is contained in:
Anders Hejlsberg
2015-01-24 08:03:29 -08:00
parent 67476f1384
commit 15dcccde7d
2 changed files with 39 additions and 39 deletions

View File

@@ -66,8 +66,8 @@ module ts {
var numberType = createIntrinsicType(TypeFlags.Number, "number");
var booleanType = createIntrinsicType(TypeFlags.Boolean, "boolean");
var voidType = createIntrinsicType(TypeFlags.Void, "void");
var undefinedType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.Unwidened, "undefined");
var nullType = createIntrinsicType(TypeFlags.Null | TypeFlags.Unwidened, "null");
var undefinedType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefined, "undefined");
var nullType = createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsUndefined, "null");
var unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
var resolvingType = createIntrinsicType(TypeFlags.Any, "__resolving__");
@@ -2807,15 +2807,19 @@ module ts {
}
}
function getUnwidenedFlagOfTypes(types: Type[]): TypeFlags {
return forEach(types, t => t.flags & TypeFlags.Unwidened) || 0;
function getWideningFlagsOfTypes(types: Type[]): TypeFlags {
var result: TypeFlags = 0;
for (var i = 0; i < types.length; i++) {
result |= types[i].flags;
}
return result & TypeFlags.RequiresWidening;
}
function createTypeReference(target: GenericType, typeArguments: Type[]): TypeReference {
var id = getTypeListId(typeArguments);
var type = target.instantiations[id];
if (!type) {
var flags = TypeFlags.Reference | getUnwidenedFlagOfTypes(typeArguments);
var flags = TypeFlags.Reference | getWideningFlagsOfTypes(typeArguments);
type = target.instantiations[id] = <TypeReference>createObjectType(flags, target.symbol);
type.target = target;
type.typeArguments = typeArguments;
@@ -3076,7 +3080,7 @@ module ts {
var id = getTypeListId(sortedTypes);
var type = unionTypes[id];
if (!type) {
type = unionTypes[id] = <UnionType>createObjectType(TypeFlags.Union | getUnwidenedFlagOfTypes(sortedTypes));
type = unionTypes[id] = <UnionType>createObjectType(TypeFlags.Union | getWideningFlagsOfTypes(sortedTypes));
type.types = sortedTypes;
}
return type;
@@ -3692,12 +3696,13 @@ module ts {
}
var result = Ternary.True;
var properties = getPropertiesOfObjectType(target);
var requireOptionalProperties = relation === subtypeRelation && !(source.flags & TypeFlags.ObjectLiteral);
for (var i = 0; i < properties.length; i++) {
var targetProp = properties[i];
var sourceProp = getPropertyOfType(source, targetProp.name);
if (sourceProp !== targetProp) {
if (!sourceProp) {
if (relation === subtypeRelation || !(targetProp.flags & SymbolFlags.Optional)) {
if (!(targetProp.flags & SymbolFlags.Optional) || requireOptionalProperties) {
if (reportErrors) {
reportError(Diagnostics.Property_0_is_missing_in_type_1, symbolToString(targetProp), typeToString(source));
}
@@ -4088,10 +4093,6 @@ module ts {
errorMessageChainHead);
}
function isTypeOfObjectLiteral(type: Type): boolean {
return (type.flags & TypeFlags.Anonymous) && type.symbol && (type.symbol.flags & SymbolFlags.ObjectLiteral) ? true : false;
}
function isArrayType(type: Type): boolean {
return type.flags & TypeFlags.Reference && (<TypeReference>type).target === globalArrayType;
}
@@ -4104,13 +4105,18 @@ module ts {
var properties = getPropertiesOfObjectType(type);
var members: SymbolTable = {};
forEach(properties, p => {
var symbol = <TransientSymbol>createSymbol(p.flags | SymbolFlags.Transient, p.name);
symbol.declarations = p.declarations;
symbol.parent = p.parent;
symbol.type = getWidenedType(getTypeOfSymbol(p));
symbol.target = p;
if (p.valueDeclaration) symbol.valueDeclaration = p.valueDeclaration;
members[symbol.name] = symbol;
var propType = getTypeOfSymbol(p);
var widenedType = getWidenedType(propType);
if (propType !== widenedType) {
var symbol = <TransientSymbol>createSymbol(p.flags | SymbolFlags.Transient, p.name);
symbol.declarations = p.declarations;
symbol.parent = p.parent;
symbol.type = getWidenedType(getTypeOfSymbol(p));
symbol.target = p;
if (p.valueDeclaration) symbol.valueDeclaration = p.valueDeclaration;
p = symbol;
}
members[p.name] = p;
});
var stringIndexType = getIndexTypeOfType(type, IndexKind.String);
var numberIndexType = getIndexTypeOfType(type, IndexKind.Number);
@@ -4120,16 +4126,16 @@ module ts {
}
function getWidenedType(type: Type): Type {
if (type.flags & TypeFlags.Unwidened) {
if (type.flags & TypeFlags.RequiresWidening) {
if (type.flags & (TypeFlags.Undefined | TypeFlags.Null)) {
return anyType;
}
if (type.flags & TypeFlags.ObjectLiteral) {
return getWidenedTypeOfObjectLiteral(type);
}
if (type.flags & TypeFlags.Union) {
return getUnionType(map((<UnionType>type).types, getWidenedType));
}
if (isTypeOfObjectLiteral(type)) {
return getWidenedTypeOfObjectLiteral(type);
}
if (isArrayType(type)) {
return createArrayType(getWidenedType((<TypeReference>type).typeArguments[0]));
}
@@ -4150,11 +4156,11 @@ module ts {
if (isArrayType(type)) {
return reportWideningErrorsInType((<TypeReference>type).typeArguments[0]);
}
if (isTypeOfObjectLiteral(type)) {
if (type.flags & TypeFlags.ObjectLiteral) {
var errorReported = false;
forEach(getPropertiesOfObjectType(type), p => {
var t = getTypeOfSymbol(p);
if (t.flags & TypeFlags.Unwidened) {
if (t.flags & TypeFlags.ContainsUndefined) {
if (!reportWideningErrorsInType(t)) {
error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, p.name, typeToString(getWidenedType(t)));
}
@@ -4198,7 +4204,7 @@ module ts {
}
function reportErrorsFromWidening(declaration: Declaration, type: Type) {
if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.Unwidened) {
if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsUndefined) {
// Report implicit any error within type if possible, otherwise report error on declaration
if (!reportWideningErrorsInType(type)) {
reportImplicitAnyError(declaration, type);
@@ -5455,21 +5461,10 @@ module ts {
}
}
// If object literal is contextually (but not inferentially) typed, copy missing optional properties from
// the contextual type such that the resulting type becomes a subtype in cases where only optional properties
// were omitted. There is no need to create new property objects as nothing in them needs to change.
if (contextualType && !isInferentialContext(contextualMapper)) {
forEach(getPropertiesOfObjectType(contextualType), p => {
if (p.flags & SymbolFlags.Optional && !hasProperty(properties, p.name)) {
properties[p.name] = p;
}
});
}
var stringIndexType = getIndexType(IndexKind.String);
var numberIndexType = getIndexType(IndexKind.Number);
var result = createAnonymousType(node.symbol, properties, emptyArray, emptyArray, stringIndexType, numberIndexType);
result.flags |= (typeFlags & TypeFlags.Unwidened);
result.flags |= TypeFlags.ObjectLiteral | TypeFlags.ContainsLiteral | (typeFlags & TypeFlags.ContainsUndefined);
return result;
function getIndexType(kind: IndexKind) {
@@ -5485,7 +5480,9 @@ module ts {
}
}
}
return propTypes.length ? getUnionType(propTypes) : undefinedType;
var result = propTypes.length ? getUnionType(propTypes) : undefinedType;
typeFlags |= result.flags;
return result;
}
return undefined;
}

View File

@@ -1283,12 +1283,15 @@ module ts {
Union = 0x00004000, // Union
Anonymous = 0x00008000, // Anonymous
FromSignature = 0x00010000, // Created for signature assignment check
Unwidened = 0x00020000, // Unwidened type (is or contains Undefined or Null type)
ObjectLiteral = 0x00020000, // Originates in an object literal
ContainsUndefined = 0x00040000, // Type is or contains Undefined or Null type
ContainsLiteral = 0x00080000, // Type is or contains object literal type
Intrinsic = Any | String | Number | Boolean | Void | Undefined | Null,
StringLike = String | StringLiteral,
NumberLike = Number | Enum,
ObjectType = Class | Interface | Reference | Tuple | Anonymous,
RequiresWidening = ContainsUndefined | ContainsLiteral
}
// Properties common to all types