@@ -2617,8 +2617,8 @@ namespace ts {
2617
2617
2618
2618
function createMappedTypeNodeFromType(type: MappedType) {
2619
2619
Debug.assert(!!(type.flags & TypeFlags.Object));
2620
- const readonlyToken = type.declaration && type.declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined;
2621
- const questionToken = type.declaration && type.declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined;
2620
+ const readonlyToken = isReadonlyMappedType( type) ? createToken(SyntaxKind.ReadonlyKeyword) : undefined;
2621
+ const questionToken = isOptionalMappedType( type) ? createToken(SyntaxKind.QuestionToken) : undefined;
2622
2622
const typeParameterNode = typeParameterToDeclaration(getTypeParameterFromMappedType(type), context);
2623
2623
const templateTypeNode = typeToTypeNodeHelper(getTemplateTypeFromMappedType(type), context);
2624
2624
@@ -3689,7 +3689,7 @@ namespace ts {
3689
3689
writePunctuation(writer, SyntaxKind.OpenBraceToken);
3690
3690
writer.writeLine();
3691
3691
writer.increaseIndent();
3692
- if (type.declaration.readonlyToken ) {
3692
+ if (isReadonlyMappedType( type) ) {
3693
3693
writeKeyword(writer, SyntaxKind.ReadonlyKeyword);
3694
3694
writeSpace(writer);
3695
3695
}
@@ -3700,7 +3700,7 @@ namespace ts {
3700
3700
writeSpace(writer);
3701
3701
writeType(getConstraintTypeFromMappedType(type), TypeFormatFlags.None);
3702
3702
writePunctuation(writer, SyntaxKind.CloseBracketToken);
3703
- if (type.declaration.questionToken ) {
3703
+ if (isOptionalMappedType( type) ) {
3704
3704
writePunctuation(writer, SyntaxKind.QuestionToken);
3705
3705
}
3706
3706
writePunctuation(writer, SyntaxKind.ColonToken);
@@ -6032,11 +6032,9 @@ namespace ts {
6032
6032
const constraintType = getConstraintTypeFromMappedType(type);
6033
6033
const templateType = getTemplateTypeFromMappedType(type);
6034
6034
const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T'
6035
- const templateReadonly = !!type.declaration.readonlyToken;
6036
- const templateOptional = !!type.declaration.questionToken;
6037
- const constraintDeclaration = type.declaration.typeParameter.constraint;
6038
- if (constraintDeclaration.kind === SyntaxKind.TypeOperator &&
6039
- (<TypeOperatorNode>constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) {
6035
+ const templateReadonly = isReadonlyMappedType(type);
6036
+ const templateOptional = isOptionalMappedType(type);
6037
+ if (isPossiblyHomomorphicMappedType(type)) {
6040
6038
// We have a { [P in keyof T]: X }
6041
6039
for (const propertySymbol of getPropertiesOfType(modifiersType)) {
6042
6040
addMemberForKeyType(getLiteralTypeFromPropertyName(propertySymbol), propertySymbol);
@@ -6104,15 +6102,14 @@ namespace ts {
6104
6102
function getTemplateTypeFromMappedType(type: MappedType) {
6105
6103
return type.templateType ||
6106
6104
(type.templateType = type.declaration.type ?
6107
- instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), !! type.declaration.questionToken ), type.mapper || identityMapper) :
6105
+ instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), isOptionalMappedType( type) ), type.mapper || identityMapper) :
6108
6106
unknownType);
6109
6107
}
6110
6108
6111
6109
function getModifiersTypeFromMappedType(type: MappedType) {
6112
6110
if (!type.modifiersType) {
6113
- const constraintDeclaration = type.declaration.typeParameter.constraint;
6114
- if (constraintDeclaration.kind === SyntaxKind.TypeOperator &&
6115
- (<TypeOperatorNode>constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) {
6111
+ if (isPossiblyHomomorphicMappedType(type)) {
6112
+ const constraintDeclaration = type.declaration.typeParameter.constraint;
6116
6113
// If the constraint declaration is a 'keyof T' node, the modifiers type is T. We check
6117
6114
// AST nodes here because, when T is a non-generic type, the logic below eagerly resolves
6118
6115
// 'keyof T' to a literal union type and we can't recover T from that type.
@@ -6132,8 +6129,8 @@ namespace ts {
6132
6129
}
6133
6130
6134
6131
function getMappedTypeModifiers(type: MappedType): MappedTypeModifiers {
6135
- return (type.declaration.readonlyToken ? MappedTypeModifiers.Readonly : 0) |
6136
- (type.declaration.questionToken ? MappedTypeModifiers.Optional : 0);
6132
+ return (isReadonlyMappedType( type) ? MappedTypeModifiers.Readonly : 0) |
6133
+ (isOptionalMappedType( type) ? MappedTypeModifiers.Optional : 0);
6137
6134
}
6138
6135
6139
6136
function getCombinedMappedTypeModifiers(type: MappedType): MappedTypeModifiers {
@@ -6143,7 +6140,7 @@ namespace ts {
6143
6140
}
6144
6141
6145
6142
function isPartialMappedType(type: Type) {
6146
- return getObjectFlags(type) & ObjectFlags.Mapped && !!(<MappedType> type).declaration.questionToken ;
6143
+ return getObjectFlags(type) & ObjectFlags.Mapped && isOptionalMappedType( type as MappedType) ;
6147
6144
}
6148
6145
6149
6146
function isGenericMappedType(type: Type) {
@@ -9738,7 +9735,7 @@ namespace ts {
9738
9735
if (target.flags & TypeFlags.TypeParameter) {
9739
9736
// A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P].
9740
9737
if (getObjectFlags(source) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(<MappedType>source) === getIndexType(target)) {
9741
- if (!(<MappedType>source).declaration.questionToken ) {
9738
+ if (!isOptionalMappedType (<MappedType>source)) {
9742
9739
const templateType = getTemplateTypeFromMappedType(<MappedType>source);
9743
9740
const indexedAccessType = getIndexedAccessType(target, getTypeParameterFromMappedType(<MappedType>source));
9744
9741
if (result = isRelatedTo(templateType, indexedAccessType, reportErrors)) {
@@ -11050,7 +11047,8 @@ namespace ts {
11050
11047
inferredType: undefined,
11051
11048
priority: undefined,
11052
11049
topLevel: true,
11053
- isFixed: false
11050
+ isFixed: false,
11051
+ indexes: undefined,
11054
11052
};
11055
11053
}
11056
11054
@@ -11061,7 +11059,8 @@ namespace ts {
11061
11059
inferredType: inference.inferredType,
11062
11060
priority: inference.priority,
11063
11061
topLevel: inference.topLevel,
11064
- isFixed: inference.isFixed
11062
+ isFixed: inference.isFixed,
11063
+ indexes: inference.indexes && inference.indexes.slice(),
11065
11064
};
11066
11065
}
11067
11066
@@ -11124,8 +11123,8 @@ namespace ts {
11124
11123
const inference = createInferenceInfo(typeParameter);
11125
11124
const inferences = [inference];
11126
11125
const templateType = getTemplateTypeFromMappedType(target);
11127
- const readonlyMask = target.declaration.readonlyToken ? false : true;
11128
- const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
11126
+ const readonlyMask = isReadonlyMappedType( target) ? false : true;
11127
+ const optionalMask = isOptionalMappedType( target) ? 0 : SymbolFlags.Optional;
11129
11128
const members = createSymbolTable();
11130
11129
for (const prop of properties) {
11131
11130
const propType = getTypeOfSymbol(prop);
@@ -11252,6 +11251,27 @@ namespace ts {
11252
11251
}
11253
11252
return;
11254
11253
}
11254
+ else if (target.flags & TypeFlags.IndexedAccess) {
11255
+ const targetConstraint = (<IndexedAccessType>target).objectType;
11256
+ const inference = getInferenceInfoForType(targetConstraint);
11257
+ if (inference) {
11258
+ if (!inference.isFixed) {
11259
+ const map = <MappedType>createObjectType(ObjectFlags.Mapped);
11260
+ map.templateType = source;
11261
+ map.constraintType = (<IndexedAccessType>target).indexType;
11262
+ map.typeParameter = <TypeParameter>createType(TypeFlags.TypeParameter);
11263
+ // TODO (weswigham): Ensure the name chosen for the unused "K" does not shadow any other type variables in the given scope, so as to not have a chance of breaking declaration emit
11264
+ map.typeParameter.symbol = createSymbol(SymbolFlags.TypeParameter, "K" as __String);
11265
+ map.typeParameter.constraint = map.constraintType;
11266
+ map.modifiersType = (<IndexedAccessType>target).indexType;
11267
+ map.hasQuestionToken = false;
11268
+ map.hasReadonlyToken = false;
11269
+ map.hasPossiblyHomomorphicConstraint = false;
11270
+ (inference.indexes || (inference.indexes = [])).push(map);
11271
+ }
11272
+ return;
11273
+ }
11274
+ }
11255
11275
}
11256
11276
else if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
11257
11277
// If source and target are references to the same generic type, infer from type arguments
@@ -11510,6 +11530,10 @@ namespace ts {
11510
11530
const inference = context.inferences[index];
11511
11531
let inferredType = inference.inferredType;
11512
11532
if (!inferredType) {
11533
+ if (inference.indexes) {
11534
+ // Build a candidate from all indexes
11535
+ (inference.candidates || (inference.candidates = [])).push(getIntersectionType(inference.indexes));
11536
+ }
11513
11537
if (inference.candidates) {
11514
11538
// Extract all object literal types and replace them with a single widened and normalized type.
11515
11539
const candidates = widenObjectLiteralCandidates(inference.candidates);
@@ -19818,6 +19842,28 @@ namespace ts {
19818
19842
forEach(node.types, checkSourceElement);
19819
19843
}
19820
19844
19845
+ function isReadonlyMappedType(type: MappedType) {
19846
+ if (type.hasReadonlyToken === undefined) {
19847
+ type.hasReadonlyToken = !!type.declaration.readonlyToken;
19848
+ }
19849
+ return type.hasReadonlyToken;
19850
+ }
19851
+
19852
+ function isOptionalMappedType(type: MappedType) {
19853
+ if (type.hasQuestionToken === undefined) {
19854
+ type.hasQuestionToken = !!type.declaration.questionToken;
19855
+ }
19856
+ return type.hasQuestionToken;
19857
+ }
19858
+
19859
+ function isPossiblyHomomorphicMappedType(type: MappedType) {
19860
+ if (type.hasPossiblyHomomorphicConstraint === undefined) {
19861
+ const constraint = type.declaration.typeParameter.constraint;
19862
+ type.hasPossiblyHomomorphicConstraint = isTypeOperatorNode(constraint) && constraint.operator === SyntaxKind.KeyOfKeyword;
19863
+ }
19864
+ return type.hasPossiblyHomomorphicConstraint;
19865
+ }
19866
+
19821
19867
function checkIndexedAccessIndexType(type: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode) {
19822
19868
if (!(type.flags & TypeFlags.IndexedAccess)) {
19823
19869
return type;
@@ -19827,7 +19873,7 @@ namespace ts {
19827
19873
const indexType = (<IndexedAccessType>type).indexType;
19828
19874
if (isTypeAssignableTo(indexType, getIndexType(objectType))) {
19829
19875
if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) &&
19830
- getObjectFlags(objectType) & ObjectFlags.Mapped && (<MappedType>objectType).declaration.readonlyToken ) {
19876
+ getObjectFlags(objectType) & ObjectFlags.Mapped && isReadonlyMappedType (<MappedType>objectType)) {
19831
19877
error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType));
19832
19878
}
19833
19879
return type;
0 commit comments