@@ -164,6 +164,7 @@ namespace ts {
164
164
anyFunctionType.flags |= TypeFlags.ContainsAnyFunctionType;
165
165
166
166
const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
167
+ const circularConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
167
168
168
169
const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
169
170
const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
@@ -4135,9 +4136,6 @@ namespace ts {
4135
4136
if (!links.declaredType) {
4136
4137
const type = <TypeParameter>createType(TypeFlags.TypeParameter);
4137
4138
type.symbol = symbol;
4138
- if (!(<TypeParameterDeclaration>getDeclarationOfKind(symbol, SyntaxKind.TypeParameter)).constraint) {
4139
- type.constraint = noConstraintType;
4140
- }
4141
4139
links.declaredType = type;
4142
4140
}
4143
4141
return <TypeParameter>links.declaredType;
@@ -4754,19 +4752,79 @@ namespace ts {
4754
4752
getPropertiesOfObjectType(type);
4755
4753
}
4756
4754
4755
+ function getConstraintOfTypeVariable(type: TypeVariable): Type {
4756
+ return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>type) : getBaseConstraintOfTypeVariable(type);
4757
+ }
4758
+
4759
+ function getConstraintOfTypeParameter(typeParameter: TypeParameter): Type {
4760
+ return hasNonCircularBaseConstraint(typeParameter) ? getConstraintFromTypeParameter(typeParameter) : undefined;
4761
+ }
4762
+
4763
+ function getBaseConstraintOfTypeVariable(type: TypeVariable): Type {
4764
+ const constraint = getResolvedBaseConstraint(type);
4765
+ return constraint !== noConstraintType && constraint !== circularConstraintType ? constraint : undefined;
4766
+ }
4767
+
4768
+ function hasNonCircularBaseConstraint(type: TypeVariable): boolean {
4769
+ return getResolvedBaseConstraint(type) !== circularConstraintType;
4770
+ }
4771
+
4757
4772
/**
4758
- * The apparent type of a type parameter is the base constraint instantiated with the type parameter
4759
- * as the type argument for the 'this' type.
4773
+ * Return the resolved base constraint of a type variable. The noConstraintType singleton is returned if the
4774
+ * type variable has no constraint, and the circularConstraintType singleton is returned if the constraint
4775
+ * circularly references the type variable.
4760
4776
*/
4761
- function getApparentTypeOfTypeVariable(type: TypeVariable) {
4777
+ function getResolvedBaseConstraint(type: TypeVariable): Type {
4778
+ let typeStack: Type[];
4779
+ let circular: boolean;
4762
4780
if (!type.resolvedApparentType) {
4763
- let constraintType = getConstraintOfTypeVariable(type);
4764
- while (constraintType && constraintType.flags & TypeFlags.TypeParameter) {
4765
- constraintType = getConstraintOfTypeVariable(<TypeVariable>constraintType);
4766
- }
4767
- type.resolvedApparentType = getTypeWithThisArgument(constraintType || emptyObjectType, type);
4781
+ typeStack = [];
4782
+ const constraint = getBaseConstraint(type);
4783
+ type.resolvedApparentType = circular ? circularConstraintType : getTypeWithThisArgument(constraint || noConstraintType, type);
4768
4784
}
4769
4785
return type.resolvedApparentType;
4786
+
4787
+ function getBaseConstraint(t: Type): Type {
4788
+ if (contains(typeStack, t)) {
4789
+ circular = true;
4790
+ return undefined;
4791
+ }
4792
+ typeStack.push(t);
4793
+ const result = computeBaseConstraint(t);
4794
+ typeStack.pop();
4795
+ return result;
4796
+ }
4797
+
4798
+ function computeBaseConstraint(t: Type): Type {
4799
+ if (t.flags & TypeFlags.TypeParameter) {
4800
+ const constraint = getConstraintFromTypeParameter(<TypeParameter>t);
4801
+ return (<TypeParameter>t).isThisType ? constraint :
4802
+ constraint ? getBaseConstraint(constraint) : undefined;
4803
+ }
4804
+ if (t.flags & TypeFlags.UnionOrIntersection) {
4805
+ const types = (<UnionOrIntersectionType>t).types;
4806
+ const baseTypes: Type[] = [];
4807
+ for (const type of types) {
4808
+ const baseType = getBaseConstraint(type);
4809
+ if (baseType) {
4810
+ baseTypes.push(baseType);
4811
+ }
4812
+ }
4813
+ return t.flags & TypeFlags.Union && baseTypes.length === types.length ? getUnionType(baseTypes) :
4814
+ t.flags & TypeFlags.Intersection && baseTypes.length ? getIntersectionType(baseTypes) :
4815
+ undefined;
4816
+ }
4817
+ if (t.flags & TypeFlags.Index) {
4818
+ return stringType;
4819
+ }
4820
+ if (t.flags & TypeFlags.IndexedAccess) {
4821
+ const baseObjectType = getBaseConstraint((<IndexedAccessType>t).objectType);
4822
+ const baseIndexType = getBaseConstraint((<IndexedAccessType>t).indexType);
4823
+ const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType) : undefined;
4824
+ return baseIndexedAccess && baseIndexedAccess !== unknownType ? getBaseConstraint(baseIndexedAccess) : undefined;
4825
+ }
4826
+ return t;
4827
+ }
4770
4828
}
4771
4829
4772
4830
/**
@@ -4775,7 +4833,7 @@ namespace ts {
4775
4833
* type itself. Note that the apparent type of a union type is the union type itself.
4776
4834
*/
4777
4835
function getApparentType(type: Type): Type {
4778
- const t = type.flags & TypeFlags.TypeVariable ? getApparentTypeOfTypeVariable (<TypeVariable>type) : type;
4836
+ const t = type.flags & TypeFlags.TypeVariable ? getBaseConstraintOfTypeVariable (<TypeVariable>type) || emptyObjectType : type;
4779
4837
return t.flags & TypeFlags.StringLike ? globalStringType :
4780
4838
t.flags & TypeFlags.NumberLike ? globalNumberType :
4781
4839
t.flags & TypeFlags.BooleanLike ? globalBooleanType :
@@ -5329,44 +5387,20 @@ namespace ts {
5329
5387
return (<TypeParameterDeclaration>getDeclarationOfKind(type.symbol, SyntaxKind.TypeParameter)).constraint;
5330
5388
}
5331
5389
5332
- function hasConstraintReferenceTo(type: Type, target: TypeParameter): boolean {
5333
- let checked: Type[];
5334
- while (type && type.flags & TypeFlags.TypeParameter && !((type as TypeParameter).isThisType) && !contains(checked, type)) {
5335
- if (type === target) {
5336
- return true;
5337
- }
5338
- (checked || (checked = [])).push(type);
5339
- const constraintDeclaration = getConstraintDeclaration(<TypeParameter>type);
5340
- type = constraintDeclaration && getTypeFromTypeNode(constraintDeclaration);
5341
- }
5342
- return false;
5343
- }
5344
-
5345
- function getConstraintOfTypeParameter(typeParameter: TypeParameter): Type {
5390
+ function getConstraintFromTypeParameter(typeParameter: TypeParameter): Type {
5346
5391
if (!typeParameter.constraint) {
5347
5392
if (typeParameter.target) {
5348
5393
const targetConstraint = getConstraintOfTypeParameter(typeParameter.target);
5349
5394
typeParameter.constraint = targetConstraint ? instantiateType(targetConstraint, typeParameter.mapper) : noConstraintType;
5350
5395
}
5351
5396
else {
5352
5397
const constraintDeclaration = getConstraintDeclaration(typeParameter);
5353
- let constraint = getTypeFromTypeNode(constraintDeclaration);
5354
- if (hasConstraintReferenceTo(constraint, typeParameter)) {
5355
- error(constraintDeclaration, Diagnostics.Type_parameter_0_has_a_circular_constraint, typeToString(typeParameter));
5356
- constraint = unknownType;
5357
- }
5358
- typeParameter.constraint = constraint;
5398
+ typeParameter.constraint = constraintDeclaration ? getTypeFromTypeNode(constraintDeclaration) : noConstraintType;
5359
5399
}
5360
5400
}
5361
5401
return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint;
5362
5402
}
5363
5403
5364
- function getConstraintOfTypeVariable(type: TypeVariable): Type {
5365
- return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>type) :
5366
- type.flags & TypeFlags.IndexedAccess ? (<IndexedAccessType>type).constraint :
5367
- undefined;
5368
- }
5369
-
5370
5404
function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol {
5371
5405
return getSymbolOfNode(getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter).parent);
5372
5406
}
@@ -6042,24 +6076,6 @@ namespace ts {
6042
6076
const type = <IndexedAccessType>createType(TypeFlags.IndexedAccess);
6043
6077
type.objectType = objectType;
6044
6078
type.indexType = indexType;
6045
- // We eagerly compute the constraint of the indexed access type such that circularity
6046
- // errors are immediately caught and reported. For example, class C { x: this["x"] }
6047
- // becomes an error only when the constraint is eagerly computed.
6048
- if (type.objectType.flags & TypeFlags.StructuredType) {
6049
- // The constraint of T[K], where T is an object, union, or intersection type,
6050
- // is the type of the string index signature of T, if any.
6051
- type.constraint = getIndexTypeOfType(type.objectType, IndexKind.String);
6052
- }
6053
- else if (type.objectType.flags & TypeFlags.TypeVariable) {
6054
- // The constraint of T[K], where T is a type variable, is A[K], where A is the
6055
- // apparent type of T.
6056
- const apparentType = getApparentTypeOfTypeVariable(<TypeVariable>type.objectType);
6057
- if (apparentType !== emptyObjectType) {
6058
- type.constraint = isTypeOfKind((<IndexedAccessType>type).indexType, TypeFlags.StringLike) ?
6059
- getIndexedAccessType(apparentType, (<IndexedAccessType>type).indexType) :
6060
- getIndexTypeOfType(apparentType, IndexKind.String);
6061
- }
6062
- }
6063
6079
return type;
6064
6080
}
6065
6081
@@ -6150,13 +6166,6 @@ namespace ts {
6150
6166
if (objectType.flags & TypeFlags.Any) {
6151
6167
return objectType;
6152
6168
}
6153
- // We first check that the index type is assignable to 'keyof T' for the object type.
6154
- if (accessNode) {
6155
- if (!isTypeAssignableTo(indexType, getIndexType(objectType))) {
6156
- error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
6157
- return unknownType;
6158
- }
6159
- }
6160
6169
// If the object type is a mapped type { [P in K]: E }, we instantiate E using a mapper that substitutes
6161
6170
// the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we construct the
6162
6171
// type Box<T[X]>.
@@ -7436,8 +7445,9 @@ namespace ts {
7436
7445
}
7437
7446
// A type S is related to a type T[K] if S is related to A[K], where K is string-like and
7438
7447
// A is the apparent type of S.
7439
- if ((<IndexedAccessType>target).constraint) {
7440
- if (result = isRelatedTo(source, (<IndexedAccessType>target).constraint, reportErrors)) {
7448
+ const constraint = getBaseConstraintOfTypeVariable(<IndexedAccessType>target);
7449
+ if (constraint) {
7450
+ if (result = isRelatedTo(source, constraint, reportErrors)) {
7441
7451
errorInfo = saveErrorInfo;
7442
7452
return result;
7443
7453
}
@@ -7475,8 +7485,9 @@ namespace ts {
7475
7485
else if (source.flags & TypeFlags.IndexedAccess) {
7476
7486
// A type S[K] is related to a type T if A[K] is related to T, where K is string-like and
7477
7487
// A is the apparent type of S.
7478
- if ((<IndexedAccessType>source).constraint) {
7479
- if (result = isRelatedTo((<IndexedAccessType>source).constraint, target, reportErrors)) {
7488
+ const constraint = getBaseConstraintOfTypeVariable(<IndexedAccessType>source);
7489
+ if (constraint) {
7490
+ if (result = isRelatedTo(constraint, target, reportErrors)) {
7480
7491
errorInfo = saveErrorInfo;
7481
7492
return result;
7482
7493
}
@@ -12528,7 +12539,7 @@ namespace ts {
12528
12539
return unknownType;
12529
12540
}
12530
12541
12531
- return getIndexedAccessType(objectType, indexType, node);
12542
+ return checkIndexedAccessIndexType( getIndexedAccessType(objectType, indexType, node) , node);
12532
12543
}
12533
12544
12534
12545
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
@@ -15186,14 +15197,14 @@ namespace ts {
15186
15197
function isLiteralContextualType(contextualType: Type) {
15187
15198
if (contextualType) {
15188
15199
if (contextualType.flags & TypeFlags.TypeVariable) {
15189
- const apparentType = getApparentTypeOfTypeVariable (<TypeVariable>contextualType);
15200
+ const constraint = getBaseConstraintOfTypeVariable (<TypeVariable>contextualType) || emptyObjectType ;
15190
15201
// If the type parameter is constrained to the base primitive type we're checking for,
15191
15202
// consider this a literal context. For example, given a type parameter 'T extends string',
15192
15203
// this causes us to infer string literal types for T.
15193
- if (apparentType .flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.Boolean | TypeFlags.Enum)) {
15204
+ if (constraint .flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.Boolean | TypeFlags.Enum)) {
15194
15205
return true;
15195
15206
}
15196
- contextualType = apparentType ;
15207
+ contextualType = constraint ;
15197
15208
}
15198
15209
return maybeTypeOfKind(contextualType, (TypeFlags.Literal | TypeFlags.Index));
15199
15210
}
@@ -15391,6 +15402,10 @@ namespace ts {
15391
15402
}
15392
15403
15393
15404
checkSourceElement(node.constraint);
15405
+ const typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node));
15406
+ if (!hasNonCircularBaseConstraint(typeParameter)) {
15407
+ error(node.constraint, Diagnostics.Type_parameter_0_has_a_circular_constraint, typeToString(typeParameter));
15408
+ }
15394
15409
getConstraintOfTypeParameter(getDeclaredTypeOfTypeParameter(getSymbolOfNode(node)));
15395
15410
if (produceDiagnostics) {
15396
15411
checkTypeNameIsReserved(node.name, Diagnostics.Type_parameter_name_cannot_be_0);
@@ -16014,17 +16029,28 @@ namespace ts {
16014
16029
forEach(node.types, checkSourceElement);
16015
16030
}
16016
16031
16032
+ function checkIndexedAccessIndexType(type: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode) {
16033
+ if (type.flags & TypeFlags.IndexedAccess) {
16034
+ // Check that the index type is assignable to 'keyof T' for the object type.
16035
+ const objectType = (<IndexedAccessType>type).objectType;
16036
+ const indexType = (<IndexedAccessType>type).indexType;
16037
+ if (!isTypeAssignableTo(indexType, getIndexType(objectType))) {
16038
+ error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
16039
+ }
16040
+ }
16041
+ return type;
16042
+ }
16043
+
16017
16044
function checkIndexedAccessType(node: IndexedAccessTypeNode) {
16018
- getTypeFromIndexedAccessTypeNode(node);
16045
+ checkIndexedAccessIndexType( getTypeFromIndexedAccessTypeNode(node), node);
16019
16046
}
16020
16047
16021
16048
function checkMappedType(node: MappedTypeNode) {
16022
16049
checkSourceElement(node.typeParameter);
16023
16050
checkSourceElement(node.type);
16024
16051
const type = <MappedType>getTypeFromMappedTypeNode(node);
16025
16052
const constraintType = getConstraintTypeFromMappedType(type);
16026
- const keyType = constraintType.flags & TypeFlags.TypeVariable ? getApparentTypeOfTypeVariable(<TypeVariable>constraintType) : constraintType;
16027
- checkTypeAssignableTo(keyType, stringType, node.typeParameter.constraint);
16053
+ checkTypeAssignableTo(constraintType, stringType, node.typeParameter.constraint);
16028
16054
}
16029
16055
16030
16056
function isPrivateWithinAmbient(node: Node): boolean {
0 commit comments