Skip to content

Commit 7d1a980

Browse files
committed
First attempt. Basically broken.
1 parent f06d571 commit 7d1a980

2 files changed

Lines changed: 52 additions & 41 deletions

File tree

src/compiler/checker.ts

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ namespace ts {
334334
const jsObjectLiteralIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
335335

336336
const globals = createSymbolTable();
337+
const deferredInferenceCache = createMap<Type | undefined>();
337338
let ambientModulesCache: Symbol[] | undefined;
338339
/**
339340
* List of every ambient module with a "*" wildcard.
@@ -4867,6 +4868,9 @@ namespace ts {
48674868
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
48684869
return getTypeOfInstantiatedSymbol(symbol);
48694870
}
4871+
if (getCheckFlags(symbol) & CheckFlags.DeferredInferred) {
4872+
return inferTargetType((symbol as DeferredTransientSymbol).propertyType, (symbol as DeferredTransientSymbol).mappedType);
4873+
}
48704874
if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) {
48714875
return getTypeOfVariableOrParameterOrProperty(symbol);
48724876
}
@@ -11224,42 +11228,49 @@ namespace ts {
1122411228
* property is computed by inferring from the source property type to X for the type
1122511229
* variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for).
1122611230
*/
11227-
function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, mappedTypeStack: string[]): Type {
11228-
const properties = getPropertiesOfType(source);
11229-
let indexInfo = getIndexInfoOfType(source, IndexKind.String);
11230-
if (properties.length === 0 && !indexInfo) {
11231-
return undefined;
11232-
}
11233-
const typeParameter = <TypeParameter>getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
11234-
const inference = createInferenceInfo(typeParameter);
11235-
const inferences = [inference];
11236-
const templateType = getTemplateTypeFromMappedType(target);
11237-
const readonlyMask = target.declaration.readonlyToken ? false : true;
11238-
const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
11239-
const members = createSymbolTable();
11240-
for (const prop of properties) {
11241-
const propType = getTypeOfSymbol(prop);
11242-
// If any property contains context sensitive functions that have been skipped, the source type
11243-
// is incomplete and we can't infer a meaningful input type.
11244-
if (propType.flags & TypeFlags.ContainsAnyFunctionType) {
11231+
function inferTypeForHomomorphicMappedType(source: Type, target: MappedType): Type {
11232+
const key = source.id + "," + target.id;
11233+
if (deferredInferenceCache.has(key)) {
11234+
return deferredInferenceCache.get(key);
11235+
}
11236+
deferredInferenceCache.set(key, function() {
11237+
const properties = getPropertiesOfType(source);
11238+
let indexInfo = getIndexInfoOfType(source, IndexKind.String);
11239+
if (properties.length === 0 && !indexInfo) {
1124511240
return undefined;
1124611241
}
11247-
const checkFlags = readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0;
11248-
const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags);
11249-
inferredProp.declarations = prop.declarations;
11250-
inferredProp.type = inferTargetType(propType);
11251-
members.set(prop.escapedName, inferredProp);
11252-
}
11253-
if (indexInfo) {
11254-
indexInfo = createIndexInfo(inferTargetType(indexInfo.type), readonlyMask && indexInfo.isReadonly);
11255-
}
11256-
return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined);
11242+
const readonlyMask = target.declaration.readonlyToken ? false : true;
11243+
const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
11244+
const members = createSymbolTable();
11245+
for (const prop of properties) {
11246+
const propType = getTypeOfSymbol(prop);
11247+
// If any property contains context sensitive functions that have been skipped, the source type
11248+
// is incomplete and we can't infer a meaningful input type.
11249+
if (propType.flags & TypeFlags.ContainsAnyFunctionType) {
11250+
return undefined;
11251+
}
11252+
const checkFlags = CheckFlags.DeferredInferred | (readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0);
11253+
const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags) as DeferredTransientSymbol;
11254+
inferredProp.declarations = prop.declarations;
11255+
inferredProp.propertyType = propType; // not sure I need this.
11256+
inferredProp.mappedType = target;
11257+
members.set(prop.escapedName, inferredProp);
11258+
}
11259+
if (indexInfo) {
11260+
// TODO: Defer this too. BARREL OF LAUGHS RIGHT THERE
11261+
indexInfo = createIndexInfo(inferTargetType(indexInfo.type, target), readonlyMask && indexInfo.isReadonly);
11262+
}
11263+
return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined);
11264+
}());
11265+
}
1125711266

11258-
function inferTargetType(sourceType: Type): Type {
11259-
inference.candidates = undefined;
11260-
inferTypes(inferences, sourceType, templateType, 0, mappedTypeStack);
11261-
return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : emptyObjectType;
11262-
}
11267+
function inferTargetType(sourceType: Type, target: MappedType): Type {
11268+
const typeParameter = <TypeParameter>getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
11269+
const templateType = getTemplateTypeFromMappedType(target);
11270+
const inference = createInferenceInfo(typeParameter);
11271+
inference.candidates = undefined;
11272+
inferTypes([inference], sourceType, templateType);
11273+
return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : emptyObjectType;
1126311274
}
1126411275

1126511276
function getUnmatchedProperty(source: Type, target: Type, requireOptionalProperties: boolean) {
@@ -11275,7 +11286,7 @@ namespace ts {
1127511286
return undefined;
1127611287
}
1127711288

11278-
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, mappedTypeStack?: string[]) {
11289+
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) {
1127911290
let symbolStack: Symbol[];
1128011291
let visited: Map<boolean>;
1128111292
inferFromTypes(originalSource, originalTarget);
@@ -11492,13 +11503,7 @@ namespace ts {
1149211503
// such that direct inferences to T get priority over inferences to Partial<T>, for example.
1149311504
const inference = getInferenceInfoForType((<IndexType>constraintType).type);
1149411505
if (inference && !inference.isFixed) {
11495-
const key = (source.symbol ? getSymbolId(source.symbol) + "," : "") + getSymbolId(target.symbol);
11496-
if (contains(mappedTypeStack, key)) {
11497-
return;
11498-
}
11499-
(mappedTypeStack || (mappedTypeStack = [])).push(key);
11500-
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target, mappedTypeStack);
11501-
mappedTypeStack.pop();
11506+
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target);
1150211507
if (inferredType) {
1150311508
const savePriority = priority;
1150411509
priority |= InferencePriority.MappedType;

src/compiler/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3217,6 +3217,7 @@ namespace ts {
32173217
ContainsPrivate = 1 << 8, // Synthetic property with private constituent(s)
32183218
ContainsStatic = 1 << 9, // Synthetic property with static constituent(s)
32193219
Late = 1 << 10, // Late-bound symbol for a computed property with a dynamic name
3220+
DeferredInferred = 1 << 11, // Deferred inferred property of homomorphic mapped type. It is HILARIOUS.
32203221
Synthetic = SyntheticProperty | SyntheticMethod
32213222
}
32223223

@@ -3226,6 +3227,11 @@ namespace ts {
32263227
isRestParameter?: boolean;
32273228
}
32283229

3230+
export interface DeferredTransientSymbol extends TransientSymbol {
3231+
propertyType: Type;
3232+
mappedType: MappedType;
3233+
}
3234+
32293235
export const enum InternalSymbolName {
32303236
Call = "__call", // Call signatures
32313237
Constructor = "__constructor", // Constructor implementations

0 commit comments

Comments
 (0)