From 15dcccde7ddbbb3626a3e3c2d5a53fe27650196a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 24 Jan 2015 08:03:29 -0800 Subject: [PATCH] Improved subtype compatibility for types of object literals --- src/compiler/checker.ts | 73 ++++++++++++++++++++--------------------- src/compiler/types.ts | 5 ++- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7233c2e85d5..431d81696a1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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] = 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] = createObjectType(TypeFlags.Union | getUnwidenedFlagOfTypes(sortedTypes)); + type = unionTypes[id] = 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 && (type).target === globalArrayType; } @@ -4104,13 +4105,18 @@ module ts { var properties = getPropertiesOfObjectType(type); var members: SymbolTable = {}; forEach(properties, p => { - var symbol = 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 = 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((type).types, getWidenedType)); } - if (isTypeOfObjectLiteral(type)) { - return getWidenedTypeOfObjectLiteral(type); - } if (isArrayType(type)) { return createArrayType(getWidenedType((type).typeArguments[0])); } @@ -4150,11 +4156,11 @@ module ts { if (isArrayType(type)) { return reportWideningErrorsInType((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; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b8c842641d7..474fdf30fed 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -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