From ca2768caf69d58c41719c9c0b9526efb87b02e41 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 8 Dec 2016 06:40:02 -0800 Subject: [PATCH] Property report circularity errors in indexed access types --- src/compiler/checker.ts | 88 ++++++++++++++++++++++------------------- src/compiler/types.ts | 1 + 2 files changed, 48 insertions(+), 41 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 35e034cebed..edc542052db 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3472,20 +3472,7 @@ namespace ts { } if (!popTypeResolution()) { - if ((symbol.valueDeclaration).type) { - // Variable has type annotation that circularly references the variable itself - type = unknownType; - error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, - symbolToString(symbol)); - } - else { - // Variable has initializer that circularly references the variable itself - type = anyType; - if (compilerOptions.noImplicitAny) { - error(symbol.valueDeclaration, Diagnostics._0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer, - symbolToString(symbol)); - } - } + type = reportCircularityError(symbol); } links.type = type; } @@ -3619,11 +3606,33 @@ namespace ts { function getTypeOfInstantiatedSymbol(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.type) { - links.type = instantiateType(getTypeOfSymbol(links.target), links.mapper); + if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { + return unknownType; + } + let type = instantiateType(getTypeOfSymbol(links.target), links.mapper); + if (!popTypeResolution()) { + type = reportCircularityError(symbol); + } + links.type = type; } return links.type; } + function reportCircularityError(symbol: Symbol) { + // Check if variable has type annotation that circularly references the variable itself + if ((symbol.valueDeclaration).type) { + error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, + symbolToString(symbol)); + return unknownType; + } + // Otherwise variable has initializer that circularly references the variable itself + if (compilerOptions.noImplicitAny) { + error(symbol.valueDeclaration, Diagnostics._0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer, + symbolToString(symbol)); + } + return anyType; + } + function getTypeOfSymbol(symbol: Symbol): Type { if (symbol.flags & SymbolFlags.Instantiated) { return getTypeOfInstantiatedSymbol(symbol); @@ -5270,28 +5279,9 @@ namespace ts { return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint; } - function getConstraintOfIndexedAccess(type: IndexedAccessType): Type { - // The constraint of T[K], where T is an object, union, or intersection type, - // is the type of the string index signature of T, if any. - if (type.objectType.flags & TypeFlags.StructuredType) { - return getIndexTypeOfType(type.objectType, IndexKind.String); - } - // The constraint of T[K], where T is a type variable, is A[K], where A is the - // apparent type of T. - if (type.objectType.flags & TypeFlags.TypeVariable) { - const apparentType = getApparentTypeOfTypeVariable(type.objectType); - if (apparentType !== emptyObjectType) { - return isTypeOfKind((type).indexType, TypeFlags.StringLike) ? - getIndexedAccessType(apparentType, (type).indexType) : - getIndexTypeOfType(apparentType, IndexKind.String); - } - } - return undefined; - } - function getConstraintOfTypeVariable(type: TypeVariable): Type { return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(type) : - type.flags & TypeFlags.IndexedAccess ? getConstraintOfIndexedAccess(type) : + type.flags & TypeFlags.IndexedAccess ? (type).constraint : undefined; } @@ -5970,6 +5960,24 @@ namespace ts { const type = createType(TypeFlags.IndexedAccess); type.objectType = objectType; type.indexType = indexType; + // We eagerly compute the constraint of the indexed access type such that circularity + // errors are immediately caught and reported. For example, class C { x: this["x"] } + // becomes an error only when the constraint is eagerly computed. + if (type.objectType.flags & TypeFlags.StructuredType) { + // The constraint of T[K], where T is an object, union, or intersection type, + // is the type of the string index signature of T, if any. + type.constraint = getIndexTypeOfType(type.objectType, IndexKind.String); + } + else if (type.objectType.flags & TypeFlags.TypeVariable) { + // The constraint of T[K], where T is a type variable, is A[K], where A is the + // apparent type of T. + const apparentType = getApparentTypeOfTypeVariable(type.objectType); + if (apparentType !== emptyObjectType) { + type.constraint = isTypeOfKind((type).indexType, TypeFlags.StringLike) ? + getIndexedAccessType(apparentType, (type).indexType) : + getIndexTypeOfType(apparentType, IndexKind.String); + } + } return type; } @@ -7312,9 +7320,8 @@ 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 = getConstraintOfIndexedAccess(target); - if (constraint) { - if (result = isRelatedTo(source, constraint, reportErrors)) { + if ((target).constraint) { + if (result = isRelatedTo(source, (target).constraint, reportErrors)) { errorInfo = saveErrorInfo; return result; } @@ -7352,9 +7359,8 @@ 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 = getConstraintOfIndexedAccess(source); - if (constraint) { - if (result = isRelatedTo(constraint, target, reportErrors)) { + if ((source).constraint) { + if (result = isRelatedTo((source).constraint, target, reportErrors)) { errorInfo = saveErrorInfo; return result; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0d15c758fd6..758f265b654 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2989,6 +2989,7 @@ namespace ts { export interface IndexedAccessType extends TypeVariable { objectType: Type; indexType: Type; + constraint?: Type; } // keyof T types (TypeFlags.Index)