From 014aeb3fd344df61de22d3ca3002d52040a4500b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 9 Mar 2017 11:50:07 -0800 Subject: [PATCH] Use immediate constraint instead of base constraint in T[K] relations --- src/compiler/checker.ts | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f36f471ee75..00498191f28 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3973,7 +3973,7 @@ namespace ts { return true; } if (type.flags & TypeFlags.TypeVariable) { - const constraint = getBaseConstraintOfType(type); + const constraint = getBaseConstraintOfType(type); return constraint && isValidBaseType(constraint) && isMixinConstructorType(constraint); } return false; @@ -4989,16 +4989,32 @@ namespace ts { } function getConstraintOfType(type: TypeVariable | UnionOrIntersectionType): Type { - return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(type) : getBaseConstraintOfType(type); + return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(type) : + type.flags & TypeFlags.IndexedAccess ? getConstraintOfIndexedAccess(type) : + getBaseConstraintOfType(type); } function getConstraintOfTypeParameter(typeParameter: TypeParameter): Type { return hasNonCircularBaseConstraint(typeParameter) ? getConstraintFromTypeParameter(typeParameter) : undefined; } - function getBaseConstraintOfType(type: TypeVariable | UnionOrIntersectionType): Type { - const constraint = getResolvedBaseConstraint(type); - return constraint !== noConstraintType && constraint !== circularConstraintType ? constraint : undefined; + function getConstraintOfIndexedAccess(type: IndexedAccessType) { + const baseObjectType = getBaseConstraintOfType(type.objectType); + const baseIndexType = getBaseConstraintOfType(type.indexType); + return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined; + } + + function getBaseConstraintOfType(type: Type): Type { + if (type.flags & (TypeFlags.TypeVariable | TypeFlags.UnionOrIntersection)) { + const constraint = getResolvedBaseConstraint(type); + if (constraint !== noConstraintType && constraint !== circularConstraintType) { + return constraint; + } + } + else if (type.flags & TypeFlags.Index) { + return stringType; + } + return undefined; } function hasNonCircularBaseConstraint(type: TypeVariable): boolean { @@ -5096,7 +5112,7 @@ namespace ts { * type itself. Note that the apparent type of a union type is the union type itself. */ function getApparentType(type: Type): Type { - const t = type.flags & TypeFlags.TypeVariable ? getBaseConstraintOfType(type) || emptyObjectType : type; + const t = type.flags & TypeFlags.TypeVariable ? getBaseConstraintOfType(type) || emptyObjectType : type; return t.flags & TypeFlags.Intersection ? getApparentTypeOfIntersectionType(t) : t.flags & TypeFlags.StringLike ? globalStringType : t.flags & TypeFlags.NumberLike ? globalNumberType : @@ -7921,7 +7937,7 @@ namespace ts { } // A type S is related to a type T[K] if S is related to A[K], where K is string-like and // A is the apparent type of S. - const constraint = getBaseConstraintOfType(target); + const constraint = getBaseConstraintOfType(target); if (constraint) { if (result = isRelatedTo(source, constraint, reportErrors)) { errorInfo = saveErrorInfo; @@ -7961,7 +7977,7 @@ namespace ts { else if (source.flags & TypeFlags.IndexedAccess) { // A type S[K] is related to a type T if A[K] is related to T, where K is string-like and // A is the apparent type of S. - const constraint = getBaseConstraintOfType(source); + const constraint = getConstraintOfType(source); if (constraint) { if (result = isRelatedTo(constraint, target, reportErrors)) { errorInfo = saveErrorInfo; @@ -9874,7 +9890,7 @@ namespace ts { return strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts; } if (flags & TypeFlags.TypeVariable) { - return getTypeFacts(getBaseConstraintOfType(type) || emptyObjectType); + return getTypeFacts(getBaseConstraintOfType(type) || emptyObjectType); } if (flags & TypeFlags.UnionOrIntersection) { return getTypeFactsOfTypes((type).types); @@ -10686,7 +10702,7 @@ namespace ts { return targetType; } if (type.flags & TypeFlags.TypeVariable) { - const constraint = getBaseConstraintOfType(type) || anyType; + const constraint = getBaseConstraintOfType(type) || anyType; if (isTypeSubtypeOf(targetType, constraint)) { return getIntersectionType([type, targetType]); } @@ -16234,7 +16250,7 @@ namespace ts { function isLiteralContextualType(contextualType: Type) { if (contextualType) { if (contextualType.flags & TypeFlags.TypeVariable) { - const constraint = getBaseConstraintOfType(contextualType) || emptyObjectType; + const constraint = getBaseConstraintOfType(contextualType) || emptyObjectType; // If the type parameter is constrained to the base primitive type we're checking for, // consider this a literal context. For example, given a type parameter 'T extends string', // this causes us to infer string literal types for T. @@ -17124,7 +17140,7 @@ namespace ts { // Check if we're indexing with a numeric type and the object type is a generic // type with a constraint that has a numeric index signature. if (maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && isTypeOfKind(indexType, TypeFlags.NumberLike)) { - const constraint = getBaseConstraintOfType(objectType); + const constraint = getBaseConstraintOfType(objectType); if (constraint && getIndexInfoOfType(constraint, IndexKind.Number)) { return type; }