From e17797979c85985e08a5a250594b89035d4da3be Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 12 Sep 2016 09:59:23 -0700 Subject: [PATCH] Address PR comments 1. Cache results of isEnumTypeRelatedTo 2. Make numeric literal assignment stricter again. 3. Use isEnumRelatedTo for comparing enums to each other. This provides the previous semi-structural semantics. 4. Because of the new distinction between computed enums (no union members) and union enums (no computed values => a union of enum literals), some semi-structural code moves out to the body of `isRelatedTo`. --- src/compiler/checker.ts | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f2c81970049..3d590330dd0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -332,6 +332,7 @@ namespace ts { const assignableRelation = createMap(); const comparableRelation = createMap(); const identityRelation = createMap(); + const enumRelation = createMap(); // This is for caching the result of getSymbolDisplayBuilder. Do not access directly. let _displayBuilder: SymbolDisplayBuilder; @@ -6195,7 +6196,12 @@ namespace ts { if (source === target) { return true; } + const id = source.id + "," + target.id; + if (enumRelation[id] !== undefined) { + return enumRelation[id]; + } if (source.symbol.name !== target.symbol.name || !(source.symbol.flags & SymbolFlags.RegularEnum) || !(target.symbol.flags & SymbolFlags.RegularEnum)) { + enumRelation[id] = false; return false; } const targetEnumType = getTypeOfSymbol(target.symbol); @@ -6207,10 +6213,12 @@ namespace ts { errorReporter(Diagnostics.Property_0_is_missing_in_type_1, property.name, typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType)); } + enumRelation[id] = false; return false; } } } + enumRelation[id] = true; return true; } @@ -6226,13 +6234,18 @@ namespace ts { if (source.flags & TypeFlags.Null && (!strictNullChecks || target.flags & TypeFlags.Null)) return true; if (relation === assignableRelation || relation === comparableRelation) { if (source.flags & TypeFlags.Any) return true; - if (source.flags & (TypeFlags.Number | TypeFlags.NumberLiteral) && target.flags & TypeFlags.EnumLike) return true; + if (source.flags & TypeFlags.Number && target.flags & TypeFlags.EnumLike) return true; + if (source.flags & TypeFlags.NumberLiteral && target.flags & TypeFlags.Enum) return true; if (source.flags & TypeFlags.NumberLiteral && target.flags & TypeFlags.EnumLiteral && (source).text === (target).text) return true; if (source.flags & TypeFlags.EnumLiteral && target.flags & TypeFlags.EnumLiteral && (source).text === (target).text && - (source).baseType.symbol.name === (target).baseType.symbol.name && - !isConstEnumSymbol((source).baseType.symbol) && !isConstEnumSymbol((target).baseType.symbol)) { + isEnumTypeRelatedTo((source).baseType, (target).baseType, errorReporter)) { + return true; + } + if (source.flags & TypeFlags.EnumLiteral && + target.flags & TypeFlags.Enum && + isEnumTypeRelatedTo(target, (source).baseType, errorReporter)) { return true; } } @@ -6410,6 +6423,14 @@ namespace ts { if (result = typeRelatedToSomeType(source, target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive))) { return result; } + if (source.flags & TypeFlags.Enum) { + // 1. look through the target union for a literal whose base type is related to the source + let lit = find((target).types, t => t.flags & TypeFlags.EnumLiteral && isEnumTypeRelatedTo(source, (t).baseType)); + // 2. if found, the base type is only assignable to source if all its literals are in the target union. + if (lit && (result = isRelatedTo((lit as EnumLiteralType).baseType, target, false))) { + return result; + } + } } }