Skip to content

Commit 6fb95e8

Browse files
committed
Greatly simplify partial inference type instantiation through use of an alias in lib
1 parent 28334af commit 6fb95e8

File tree

3 files changed

+38
-52
lines changed

3 files changed

+38
-52
lines changed

src/compiler/checker.ts

Lines changed: 28 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2618,8 +2618,8 @@ namespace ts {
26182618

26192619
function createMappedTypeNodeFromType(type: MappedType) {
26202620
Debug.assert(!!(type.flags & TypeFlags.Object));
2621-
const readonlyToken = isReadonlyMappedType(type) ? createToken(SyntaxKind.ReadonlyKeyword) : undefined;
2622-
const questionToken = isOptionalMappedType(type) ? createToken(SyntaxKind.QuestionToken) : undefined;
2621+
const readonlyToken = type.declaration && type.declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined;
2622+
const questionToken = type.declaration && type.declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined;
26232623
const typeParameterNode = typeParameterToDeclaration(getTypeParameterFromMappedType(type), context);
26242624
const templateTypeNode = typeToTypeNodeHelper(getTemplateTypeFromMappedType(type), context);
26252625

@@ -3690,7 +3690,7 @@ namespace ts {
36903690
writePunctuation(writer, SyntaxKind.OpenBraceToken);
36913691
writer.writeLine();
36923692
writer.increaseIndent();
3693-
if (isReadonlyMappedType(type)) {
3693+
if (type.declaration.readonlyToken) {
36943694
writeKeyword(writer, SyntaxKind.ReadonlyKeyword);
36953695
writeSpace(writer);
36963696
}
@@ -3701,7 +3701,7 @@ namespace ts {
37013701
writeSpace(writer);
37023702
writeType(getConstraintTypeFromMappedType(type), TypeFormatFlags.None);
37033703
writePunctuation(writer, SyntaxKind.CloseBracketToken);
3704-
if (isOptionalMappedType(type)) {
3704+
if (type.declaration.questionToken) {
37053705
writePunctuation(writer, SyntaxKind.QuestionToken);
37063706
}
37073707
writePunctuation(writer, SyntaxKind.ColonToken);
@@ -6038,9 +6038,11 @@ namespace ts {
60386038
const constraintType = getConstraintTypeFromMappedType(type);
60396039
const templateType = getTemplateTypeFromMappedType(type);
60406040
const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T'
6041-
const templateReadonly = isReadonlyMappedType(type);
6042-
const templateOptional = isOptionalMappedType(type);
6043-
if (isPossiblyHomomorphicMappedType(type)) {
6041+
const templateReadonly = !!type.declaration.readonlyToken;
6042+
const templateOptional = !!type.declaration.questionToken;
6043+
const constraintDeclaration = type.declaration.typeParameter.constraint;
6044+
if (constraintDeclaration.kind === SyntaxKind.TypeOperator &&
6045+
(<TypeOperatorNode>constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) {
60446046
// We have a { [P in keyof T]: X }
60456047
for (const propertySymbol of getPropertiesOfType(modifiersType)) {
60466048
addMemberForKeyType(getLiteralTypeFromPropertyName(propertySymbol), propertySymbol);
@@ -6108,14 +6110,15 @@ namespace ts {
61086110
function getTemplateTypeFromMappedType(type: MappedType) {
61096111
return type.templateType ||
61106112
(type.templateType = type.declaration.type ?
6111-
instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), isOptionalMappedType(type)), type.mapper || identityMapper) :
6113+
instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), !!type.declaration.questionToken), type.mapper || identityMapper) :
61126114
unknownType);
61136115
}
61146116

61156117
function getModifiersTypeFromMappedType(type: MappedType) {
61166118
if (!type.modifiersType) {
6117-
if (isPossiblyHomomorphicMappedType(type)) {
6118-
const constraintDeclaration = type.declaration.typeParameter.constraint;
6119+
const constraintDeclaration = type.declaration.typeParameter.constraint;
6120+
if (constraintDeclaration.kind === SyntaxKind.TypeOperator &&
6121+
(<TypeOperatorNode>constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) {
61196122
// If the constraint declaration is a 'keyof T' node, the modifiers type is T. We check
61206123
// AST nodes here because, when T is a non-generic type, the logic below eagerly resolves
61216124
// 'keyof T' to a literal union type and we can't recover T from that type.
@@ -6135,8 +6138,8 @@ namespace ts {
61356138
}
61366139

61376140
function getMappedTypeModifiers(type: MappedType): MappedTypeModifiers {
6138-
return (isReadonlyMappedType(type) ? MappedTypeModifiers.Readonly : 0) |
6139-
(isOptionalMappedType(type) ? MappedTypeModifiers.Optional : 0);
6141+
return (type.declaration.readonlyToken ? MappedTypeModifiers.Readonly : 0) |
6142+
(type.declaration.questionToken ? MappedTypeModifiers.Optional : 0);
61406143
}
61416144

61426145
function getCombinedMappedTypeModifiers(type: MappedType): MappedTypeModifiers {
@@ -6146,7 +6149,7 @@ namespace ts {
61466149
}
61476150

61486151
function isPartialMappedType(type: Type) {
6149-
return getObjectFlags(type) & ObjectFlags.Mapped && isOptionalMappedType(type as MappedType);
6152+
return getObjectFlags(type) & ObjectFlags.Mapped && !!(<MappedType>type).declaration.questionToken;
61506153
}
61516154

61526155
function isGenericMappedType(type: Type) {
@@ -9743,7 +9746,7 @@ namespace ts {
97439746
if (target.flags & TypeFlags.TypeParameter) {
97449747
// A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P].
97459748
if (getObjectFlags(source) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(<MappedType>source) === getIndexType(target)) {
9746-
if (!isOptionalMappedType(<MappedType>source)) {
9749+
if (!(<MappedType>source).declaration.questionToken) {
97479750
const templateType = getTemplateTypeFromMappedType(<MappedType>source);
97489751
const indexedAccessType = getIndexedAccessType(target, getTypeParameterFromMappedType(<MappedType>source));
97499752
if (result = isRelatedTo(templateType, indexedAccessType, reportErrors)) {
@@ -11131,8 +11134,8 @@ namespace ts {
1113111134
const inference = createInferenceInfo(typeParameter);
1113211135
const inferences = [inference];
1113311136
const templateType = getTemplateTypeFromMappedType(target);
11134-
const readonlyMask = isReadonlyMappedType(target) ? false : true;
11135-
const optionalMask = isOptionalMappedType(target) ? 0 : SymbolFlags.Optional;
11137+
const readonlyMask = target.declaration.readonlyToken ? false : true;
11138+
const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
1113611139
const members = createSymbolTable();
1113711140
for (const prop of properties) {
1113811141
const propType = getTypeOfSymbol(prop);
@@ -11264,18 +11267,14 @@ namespace ts {
1126411267
const inference = getInferenceInfoForType(targetConstraint);
1126511268
if (inference) {
1126611269
if (!inference.isFixed) {
11267-
const map = <MappedType>createObjectType(ObjectFlags.Mapped);
11268-
map.templateType = source;
11269-
map.constraintType = (<IndexedAccessType>target).indexType;
11270-
map.typeParameter = <TypeParameter>createType(TypeFlags.TypeParameter);
11271-
// 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
11272-
map.typeParameter.symbol = createSymbol(SymbolFlags.TypeParameter, "K" as __String);
11273-
map.typeParameter.constraint = map.constraintType;
11274-
map.modifiersType = (<IndexedAccessType>target).indexType;
11275-
map.hasQuestionToken = false;
11276-
map.hasReadonlyToken = false;
11277-
map.hasPossiblyHomomorphicConstraint = false;
11278-
(inference.indexes || (inference.indexes = [])).push(map);
11270+
// Instantiates instance of `type PartialInference<T, Keys extends string> = ({[K in Keys]: {[K1 in K]: T}})[Keys];`
11271+
// Where `T` is `source` and `Keys` is `target.indexType`
11272+
const inferenceTypeSymbol = getGlobalSymbol("PartialInference" as __String, SymbolFlags.Type, Diagnostics.Cannot_find_global_type_0);
11273+
const inferenceType = getDeclaredTypeOfSymbol(inferenceTypeSymbol);
11274+
if (inferenceType !== unknownType) {
11275+
const mapper = createTypeMapper(getSymbolLinks(inferenceTypeSymbol).typeParameters, [source, (target as IndexedAccessType).indexType]);
11276+
(inference.indexes || (inference.indexes = [])).push(instantiateType(inferenceType, mapper));
11277+
}
1127911278
}
1128011279
return;
1128111280
}
@@ -19857,28 +19856,6 @@ namespace ts {
1985719856
forEach(node.types, checkSourceElement);
1985819857
}
1985919858

19860-
function isReadonlyMappedType(type: MappedType) {
19861-
if (type.hasReadonlyToken === undefined) {
19862-
type.hasReadonlyToken = !!type.declaration.readonlyToken;
19863-
}
19864-
return type.hasReadonlyToken;
19865-
}
19866-
19867-
function isOptionalMappedType(type: MappedType) {
19868-
if (type.hasQuestionToken === undefined) {
19869-
type.hasQuestionToken = !!type.declaration.questionToken;
19870-
}
19871-
return type.hasQuestionToken;
19872-
}
19873-
19874-
function isPossiblyHomomorphicMappedType(type: MappedType) {
19875-
if (type.hasPossiblyHomomorphicConstraint === undefined) {
19876-
const constraint = type.declaration.typeParameter.constraint;
19877-
type.hasPossiblyHomomorphicConstraint = isTypeOperatorNode(constraint) && constraint.operator === SyntaxKind.KeyOfKeyword;
19878-
}
19879-
return type.hasPossiblyHomomorphicConstraint;
19880-
}
19881-
1988219859
function checkIndexedAccessIndexType(type: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode) {
1988319860
if (!(type.flags & TypeFlags.IndexedAccess)) {
1988419861
return type;
@@ -19888,7 +19865,7 @@ namespace ts {
1988819865
const indexType = (<IndexedAccessType>type).indexType;
1988919866
if (isTypeAssignableTo(indexType, getIndexType(objectType))) {
1989019867
if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) &&
19891-
getObjectFlags(objectType) & ObjectFlags.Mapped && isReadonlyMappedType(<MappedType>objectType)) {
19868+
getObjectFlags(objectType) & ObjectFlags.Mapped && (<MappedType>objectType).declaration.readonlyToken) {
1989219869
error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType));
1989319870
}
1989419871
return type;

src/lib/es5.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,15 @@ type Record<K extends string, T> = {
13361336
*/
13371337
interface ThisType<T> { }
13381338

1339+
/**
1340+
* Type instantiated to perform partial inferences from indexed accesses
1341+
*/
1342+
type PartialInference<T, Keys extends string> = ({
1343+
[K in Keys]: {
1344+
[K1 in K]: T
1345+
}
1346+
})[Keys];
1347+
13391348
/**
13401349
* Represents a raw buffer of binary data, which is used to store data for the
13411350
* different typed arrays. ArrayBuffers cannot be read from or written to directly,

tests/baselines/reference/inferingFromAny.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ var a = f18(a);
293293

294294
var a = f19(a, a);
295295
>a : any
296-
>f19(a, a) : { [K in K]: any; }
296+
>f19(a, a) : { [K in Keys]: { [K1 in K]: any; }; }[K]
297297
>f19 : <T, K extends keyof T>(k: K, x: T[K]) => T
298298
>a : any
299299
>a : any

0 commit comments

Comments
 (0)