Normalize object literals in widened union types

This commit is contained in:
Anders Hejlsberg 2017-10-25 11:46:51 -07:00
parent b17e75b52b
commit b435c90f86
2 changed files with 46 additions and 13 deletions

View file

@ -7984,7 +7984,7 @@ namespace ts {
const spread = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
spread.flags |= propagatedFlags;
spread.flags |= TypeFlags.FreshLiteral | TypeFlags.ContainsObjectLiteral;
(spread as ObjectType).objectFlags |= ObjectFlags.ObjectLiteral;
(spread as ObjectType).objectFlags |= (ObjectFlags.ObjectLiteral | ObjectFlags.ContainsSpread);
spread.symbol = symbol;
return spread;
}
@ -9604,13 +9604,17 @@ namespace ts {
}
return Ternary.False;
}
if (relation === subtypeRelation && target.flags & TypeFlags.FreshLiteral) {
const excessProperty = getUnmatchedProperty(target, source, /*requireOptionalProperties*/ true);
if (excessProperty) {
if (reportErrors) {
reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(excessProperty), typeToString(target));
if (getObjectFlags(target) & ObjectFlags.ObjectLiteral) {
for (const sourceProp of getPropertiesOfType(source)) {
if (!getPropertyOfObjectType(target, sourceProp.escapedName)) {
const sourceType = getTypeOfSymbol(sourceProp);
if (!(sourceType === undefinedType || sourceType === undefinedWideningType)) {
if (reportErrors) {
reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(sourceProp), typeToString(target));
}
return Ternary.False;
}
}
return Ternary.False;
}
}
let result = Ternary.True;
@ -10462,13 +10466,26 @@ namespace ts {
return widened === original ? prop : createSymbolWithType(prop, widened);
}
function getWidenedTypeOfObjectLiteral(type: Type): Type {
function getUndefinedProperty(name: __String) {
const result = createSymbol(SymbolFlags.Property | SymbolFlags.Optional, name);
result.type = undefinedType;
return result;
}
function getWidenedTypeOfObjectLiteral(type: Type, requiredNames?: __String[]): Type {
const members = createSymbolTable();
for (const prop of getPropertiesOfObjectType(type)) {
// Since get accessors already widen their return value there is no need to
// widen accessor based properties here.
members.set(prop.escapedName, prop.flags & SymbolFlags.Property ? getWidenedProperty(prop) : prop);
}
if (requiredNames && !(getObjectFlags(type) & ObjectFlags.ContainsSpread)) {
for (const name of requiredNames) {
if (!members.has(name)) {
members.set(name, getUndefinedProperty(name));
}
}
}
const stringIndexInfo = getIndexInfoOfType(type, IndexKind.String);
const numberIndexInfo = getIndexInfoOfType(type, IndexKind.Number);
return createAnonymousType(type.symbol, members, emptyArray, emptyArray,
@ -10476,20 +10493,35 @@ namespace ts {
numberIndexInfo && createIndexInfo(getWidenedType(numberIndexInfo.type), numberIndexInfo.isReadonly));
}
function getWidenedConstituentType(type: Type): Type {
return type.flags & TypeFlags.Nullable ? type : getWidenedType(type);
function getWidenedTypeOfUnion(type: UnionType) {
const types = type.types;
const names = createMap<boolean>() as UnderscoreEscapedMap<boolean>;
for (const t of types) {
const objectFlags = getObjectFlags(t);
if (objectFlags & ObjectFlags.ObjectLiteral && !(objectFlags & ObjectFlags.ContainsSpread)) {
for (const prop of getPropertiesOfType(t)) {
names.set(prop.escapedName, true);
}
}
}
const requiredNames = arrayFrom(names.keys());
return getUnionType(sameMap(types, t => t.flags & TypeFlags.Nullable ? t : getWidenedTypeWithRequiredNames(t, requiredNames)));
}
function getWidenedType(type: Type): Type {
function getWidenedType(type: Type) {
return getWidenedTypeWithRequiredNames(type, /*requiredNames*/ undefined);
}
function getWidenedTypeWithRequiredNames(type: Type, requiredNames: __String[]): Type {
if (type.flags & TypeFlags.RequiresWidening) {
if (type.flags & TypeFlags.Nullable) {
return anyType;
}
if (getObjectFlags(type) & ObjectFlags.ObjectLiteral) {
return getWidenedTypeOfObjectLiteral(type);
return getWidenedTypeOfObjectLiteral(type, requiredNames);
}
if (type.flags & TypeFlags.Union) {
return getUnionType(sameMap((<UnionType>type).types, getWidenedConstituentType));
return getWidenedTypeOfUnion(<UnionType>type);
}
if (isArrayType(type) || isTupleType(type)) {
return createTypeReference((<TypeReference>type).target, sameMap((<TypeReference>type).typeArguments, getWidenedType));

View file

@ -3327,6 +3327,7 @@ namespace ts {
ObjectLiteral = 1 << 7, // Originates in an object literal
EvolvingArray = 1 << 8, // Evolving array type
ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties
ContainsSpread = 1 << 10, // Object literal contains spread operation
ClassOrInterface = Class | Interface
}