Skip to content

Commit ca2768c

Browse files
committed
Property report circularity errors in indexed access types
1 parent d3ea738 commit ca2768c

File tree

2 files changed

+48
-41
lines changed

2 files changed

+48
-41
lines changed

src/compiler/checker.ts

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3472,20 +3472,7 @@ namespace ts {
34723472
}
34733473

34743474
if (!popTypeResolution()) {
3475-
if ((<VariableLikeDeclaration>symbol.valueDeclaration).type) {
3476-
// Variable has type annotation that circularly references the variable itself
3477-
type = unknownType;
3478-
error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation,
3479-
symbolToString(symbol));
3480-
}
3481-
else {
3482-
// Variable has initializer that circularly references the variable itself
3483-
type = anyType;
3484-
if (compilerOptions.noImplicitAny) {
3485-
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,
3486-
symbolToString(symbol));
3487-
}
3488-
}
3475+
type = reportCircularityError(symbol);
34893476
}
34903477
links.type = type;
34913478
}
@@ -3619,11 +3606,33 @@ namespace ts {
36193606
function getTypeOfInstantiatedSymbol(symbol: Symbol): Type {
36203607
const links = getSymbolLinks(symbol);
36213608
if (!links.type) {
3622-
links.type = instantiateType(getTypeOfSymbol(links.target), links.mapper);
3609+
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
3610+
return unknownType;
3611+
}
3612+
let type = instantiateType(getTypeOfSymbol(links.target), links.mapper);
3613+
if (!popTypeResolution()) {
3614+
type = reportCircularityError(symbol);
3615+
}
3616+
links.type = type;
36233617
}
36243618
return links.type;
36253619
}
36263620

3621+
function reportCircularityError(symbol: Symbol) {
3622+
// Check if variable has type annotation that circularly references the variable itself
3623+
if ((<VariableLikeDeclaration>symbol.valueDeclaration).type) {
3624+
error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation,
3625+
symbolToString(symbol));
3626+
return unknownType;
3627+
}
3628+
// Otherwise variable has initializer that circularly references the variable itself
3629+
if (compilerOptions.noImplicitAny) {
3630+
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,
3631+
symbolToString(symbol));
3632+
}
3633+
return anyType;
3634+
}
3635+
36273636
function getTypeOfSymbol(symbol: Symbol): Type {
36283637
if (symbol.flags & SymbolFlags.Instantiated) {
36293638
return getTypeOfInstantiatedSymbol(symbol);
@@ -5270,28 +5279,9 @@ namespace ts {
52705279
return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint;
52715280
}
52725281

5273-
function getConstraintOfIndexedAccess(type: IndexedAccessType): Type {
5274-
// The constraint of T[K], where T is an object, union, or intersection type,
5275-
// is the type of the string index signature of T, if any.
5276-
if (type.objectType.flags & TypeFlags.StructuredType) {
5277-
return getIndexTypeOfType(type.objectType, IndexKind.String);
5278-
}
5279-
// The constraint of T[K], where T is a type variable, is A[K], where A is the
5280-
// apparent type of T.
5281-
if (type.objectType.flags & TypeFlags.TypeVariable) {
5282-
const apparentType = getApparentTypeOfTypeVariable(<TypeVariable>type.objectType);
5283-
if (apparentType !== emptyObjectType) {
5284-
return isTypeOfKind((<IndexedAccessType>type).indexType, TypeFlags.StringLike) ?
5285-
getIndexedAccessType(apparentType, (<IndexedAccessType>type).indexType) :
5286-
getIndexTypeOfType(apparentType, IndexKind.String);
5287-
}
5288-
}
5289-
return undefined;
5290-
}
5291-
52925282
function getConstraintOfTypeVariable(type: TypeVariable): Type {
52935283
return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>type) :
5294-
type.flags & TypeFlags.IndexedAccess ? getConstraintOfIndexedAccess(<IndexedAccessType>type) :
5284+
type.flags & TypeFlags.IndexedAccess ? (<IndexedAccessType>type).constraint :
52955285
undefined;
52965286
}
52975287

@@ -5970,6 +5960,24 @@ namespace ts {
59705960
const type = <IndexedAccessType>createType(TypeFlags.IndexedAccess);
59715961
type.objectType = objectType;
59725962
type.indexType = indexType;
5963+
// We eagerly compute the constraint of the indexed access type such that circularity
5964+
// errors are immediately caught and reported. For example, class C { x: this["x"] }
5965+
// becomes an error only when the constraint is eagerly computed.
5966+
if (type.objectType.flags & TypeFlags.StructuredType) {
5967+
// The constraint of T[K], where T is an object, union, or intersection type,
5968+
// is the type of the string index signature of T, if any.
5969+
type.constraint = getIndexTypeOfType(type.objectType, IndexKind.String);
5970+
}
5971+
else if (type.objectType.flags & TypeFlags.TypeVariable) {
5972+
// The constraint of T[K], where T is a type variable, is A[K], where A is the
5973+
// apparent type of T.
5974+
const apparentType = getApparentTypeOfTypeVariable(<TypeVariable>type.objectType);
5975+
if (apparentType !== emptyObjectType) {
5976+
type.constraint = isTypeOfKind((<IndexedAccessType>type).indexType, TypeFlags.StringLike) ?
5977+
getIndexedAccessType(apparentType, (<IndexedAccessType>type).indexType) :
5978+
getIndexTypeOfType(apparentType, IndexKind.String);
5979+
}
5980+
}
59735981
return type;
59745982
}
59755983

@@ -7312,9 +7320,8 @@ namespace ts {
73127320
}
73137321
// A type S is related to a type T[K] if S is related to A[K], where K is string-like and
73147322
// A is the apparent type of S.
7315-
const constraint = getConstraintOfIndexedAccess(<IndexedAccessType>target);
7316-
if (constraint) {
7317-
if (result = isRelatedTo(source, constraint, reportErrors)) {
7323+
if ((<IndexedAccessType>target).constraint) {
7324+
if (result = isRelatedTo(source, (<IndexedAccessType>target).constraint, reportErrors)) {
73187325
errorInfo = saveErrorInfo;
73197326
return result;
73207327
}
@@ -7352,9 +7359,8 @@ namespace ts {
73527359
else if (source.flags & TypeFlags.IndexedAccess) {
73537360
// A type S[K] is related to a type T if A[K] is related to T, where K is string-like and
73547361
// A is the apparent type of S.
7355-
const constraint = getConstraintOfIndexedAccess(<IndexedAccessType>source);
7356-
if (constraint) {
7357-
if (result = isRelatedTo(constraint, target, reportErrors)) {
7362+
if ((<IndexedAccessType>source).constraint) {
7363+
if (result = isRelatedTo((<IndexedAccessType>source).constraint, target, reportErrors)) {
73587364
errorInfo = saveErrorInfo;
73597365
return result;
73607366
}

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2989,6 +2989,7 @@ namespace ts {
29892989
export interface IndexedAccessType extends TypeVariable {
29902990
objectType: Type;
29912991
indexType: Type;
2992+
constraint?: Type;
29922993
}
29932994

29942995
// keyof T types (TypeFlags.Index)

0 commit comments

Comments
 (0)