Merge pull request #12528 from Microsoft/mappedTypeInference

Type inference for isomorphic mapped types
This commit is contained in:
Anders Hejlsberg
2016-11-28 13:30:45 -08:00
committed by GitHub
5 changed files with 1140 additions and 25 deletions

View File

@@ -4494,7 +4494,6 @@ namespace ts {
function resolveMappedTypeMembers(type: MappedType) {
const members: SymbolTable = createMap<Symbol>();
let stringIndexInfo: IndexInfo;
let numberIndexInfo: IndexInfo;
// Resolve upfront such that recursive references see an empty object type.
setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined);
// In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type,
@@ -4529,16 +4528,8 @@ namespace ts {
else if (t.flags & TypeFlags.String) {
stringIndexInfo = createIndexInfo(propType, isReadonly);
}
else if (t.flags & TypeFlags.Number) {
numberIndexInfo = createIndexInfo(propType, isReadonly);
}
});
// If we created both a string and a numeric string index signature, and if the two index
// signatures have identical types, discard the redundant numeric index signature.
if (stringIndexInfo && numberIndexInfo && isTypeIdenticalTo(stringIndexInfo.type, numberIndexInfo.type)) {
numberIndexInfo = undefined;
}
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
}
function getTypeParameterFromMappedType(type: MappedType) {
@@ -8432,7 +8423,7 @@ namespace ts {
// results for union and intersection types for performance reasons.
function couldContainTypeParameters(type: Type): boolean {
const objectFlags = getObjectFlags(type);
return !!(type.flags & TypeFlags.TypeParameter ||
return !!(type.flags & (TypeFlags.TypeParameter | TypeFlags.IndexedAccess) ||
objectFlags & ObjectFlags.Reference && forEach((<TypeReference>type).typeArguments, couldContainTypeParameters) ||
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) ||
objectFlags & ObjectFlags.Mapped ||
@@ -8450,8 +8441,57 @@ namespace ts {
return type === typeParameter || type.flags & TypeFlags.UnionOrIntersection && forEach((<UnionOrIntersectionType>type).types, t => isTypeParameterAtTopLevel(t, typeParameter));
}
function inferTypes(context: InferenceContext, originalSource: Type, originalTarget: Type) {
const typeParameters = context.signature.typeParameters;
// Infer a suitable input type for an isomorphic mapped type { [P in keyof T]: X }. We construct
// an object type with the same set of properties as the source type, where the type of each
// property is computed by inferring from the source property type to X for a synthetic type
// parameter T[P] (i.e. we treat the type T[P] as the type parameter we're inferring for).
function inferTypeForIsomorphicMappedType(source: Type, target: MappedType): Type {
if (!isMappableType(source)) {
return source;
}
const typeParameter = getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
const typeParameterArray = [typeParameter];
const typeInferences = createTypeInferencesObject();
const typeInferencesArray = [typeInferences];
const templateType = getTemplateTypeFromMappedType(target);
const properties = getPropertiesOfType(source);
const members = createSymbolTable(properties);
let hasInferredTypes = false;
for (const prop of properties) {
const inferredPropType = inferTargetType(getTypeOfSymbol(prop));
if (inferredPropType) {
const inferredProp = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | prop.flags & SymbolFlags.Optional, prop.name);
inferredProp.declarations = prop.declarations;
inferredProp.type = inferredPropType;
inferredProp.isReadonly = isReadonlySymbol(prop);
members[prop.name] = inferredProp;
hasInferredTypes = true;
}
}
let indexInfo = getIndexInfoOfType(source, IndexKind.String);
if (indexInfo) {
const inferredIndexType = inferTargetType(indexInfo.type);
if (inferredIndexType) {
indexInfo = createIndexInfo(inferredIndexType, indexInfo.isReadonly);
hasInferredTypes = true;
}
}
return hasInferredTypes ? createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined) : source;
function inferTargetType(sourceType: Type): Type {
typeInferences.primary = undefined;
typeInferences.secondary = undefined;
inferTypes(typeParameterArray, typeInferencesArray, sourceType, templateType);
const inferences = typeInferences.primary || typeInferences.secondary;
return inferences && getUnionType(inferences, /*subtypeReduction*/ true);
}
}
function inferTypesWithContext(context: InferenceContext, originalSource: Type, originalTarget: Type) {
inferTypes(context.signature.typeParameters, context.inferences, originalSource, originalTarget);
}
function inferTypes(typeParameters: Type[], typeInferences: TypeInferences[], originalSource: Type, originalTarget: Type) {
let sourceStack: Type[];
let targetStack: Type[];
let depth = 0;
@@ -8519,7 +8559,7 @@ namespace ts {
target = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
}
}
if (target.flags & TypeFlags.TypeParameter) {
if (target.flags & (TypeFlags.TypeParameter | TypeFlags.IndexedAccess)) {
// If target is a type parameter, make an inference, unless the source type contains
// the anyFunctionType (the wildcard type that's used to avoid contextually typing functions).
// Because the anyFunctionType is internal, it should not be exposed to the user by adding
@@ -8531,7 +8571,7 @@ namespace ts {
}
for (let i = 0; i < typeParameters.length; i++) {
if (target === typeParameters[i]) {
const inferences = context.inferences[i];
const inferences = typeInferences[i];
if (!inferences.isFixed) {
// Any inferences that are made to a type parameter in a union type are inferior
// to inferences made to a flat (non-union) type. This is because if we infer to
@@ -8545,7 +8585,7 @@ namespace ts {
if (!contains(candidates, source)) {
candidates.push(source);
}
if (!isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
if (target.flags & TypeFlags.TypeParameter && !isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
inferences.topLevel = false;
}
}
@@ -8622,12 +8662,19 @@ namespace ts {
function inferFromObjectTypes(source: Type, target: Type) {
if (getObjectFlags(target) & ObjectFlags.Mapped) {
const constraintType = getConstraintTypeFromMappedType(<MappedType>target);
if (getObjectFlags(source) & ObjectFlags.Mapped) {
inferFromTypes(getConstraintTypeFromMappedType(<MappedType>source), constraintType);
inferFromTypes(getTemplateTypeFromMappedType(<MappedType>source), getTemplateTypeFromMappedType(<MappedType>target));
if (constraintType.flags & TypeFlags.Index) {
// We're inferring from some source type S to an isomorphic mapped type { [P in keyof T]: X },
// where T is a type parameter. Use inferTypeForIsomorphicMappedType to infer a suitable source
// type and then infer from that type to T.
const index = indexOf(typeParameters, (<IndexType>constraintType).type);
if (index >= 0 && !typeInferences[index].isFixed) {
inferFromTypes(inferTypeForIsomorphicMappedType(source, <MappedType>target), typeParameters[index]);
}
return;
}
if (constraintType.flags & TypeFlags.TypeParameter) {
// We're inferring from some source type S to a mapped type { [P in T]: X }, where T is a type
// parameter. Infer from 'keyof S' to T and infer from a union of each property type in S to X.
inferFromTypes(getIndexType(source), constraintType);
inferFromTypes(getUnionType(map(getPropertiesOfType(source), getTypeOfSymbol)), getTemplateTypeFromMappedType(<MappedType>target));
return;
@@ -12469,7 +12516,7 @@ namespace ts {
const context = createInferenceContext(signature, /*inferUnionTypes*/ true);
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
inferTypes(context, instantiateType(source, contextualMapper), target);
inferTypesWithContext(context, instantiateType(source, contextualMapper), target);
});
return getSignatureInstantiation(signature, getInferredTypes(context));
}
@@ -12504,7 +12551,7 @@ namespace ts {
if (thisType) {
const thisArgumentNode = getThisArgumentOfCall(node);
const thisArgumentType = thisArgumentNode ? checkExpression(thisArgumentNode) : voidType;
inferTypes(context, thisArgumentType, thisType);
inferTypesWithContext(context, thisArgumentType, thisType);
}
// We perform two passes over the arguments. In the first pass we infer from all arguments, but use
@@ -12526,7 +12573,7 @@ namespace ts {
argType = checkExpressionWithContextualType(arg, paramType, mapper);
}
inferTypes(context, argType, paramType);
inferTypesWithContext(context, argType, paramType);
}
}
@@ -12541,7 +12588,7 @@ namespace ts {
if (excludeArgument[i] === false) {
const arg = args[i];
const paramType = getTypeAtPosition(signature, i);
inferTypes(context, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType);
inferTypesWithContext(context, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType);
}
}
}
@@ -13628,7 +13675,7 @@ namespace ts {
for (let i = 0; i < len; i++) {
const declaration = <ParameterDeclaration>signature.parameters[i].valueDeclaration;
if (declaration.type) {
inferTypes(mapper.context, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
inferTypesWithContext(mapper.context, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
}
}
}
@@ -13714,7 +13761,7 @@ namespace ts {
// T in the second overload so that we do not infer Base as a candidate for T
// (inferring Base would make type argument inference inconsistent between the two
// overloads).
inferTypes(mapper.context, links.type, instantiateType(contextualType, mapper));
inferTypesWithContext(mapper.context, links.type, instantiateType(contextualType, mapper));
}
}

View File

@@ -0,0 +1,222 @@
//// [isomorphicMappedTypeInference.ts]
type Box<T> = {
value: T;
}
type Boxified<T> = {
[P in keyof T]: Box<T[P]>;
}
function box<T>(x: T): Box<T> {
return { value: x };
}
function unbox<T>(x: Box<T>): T {
return x.value;
}
function boxify<T>(obj: T): Boxified<T> {
let result = {} as Boxified<T>;
for (let k in obj) {
result[k] = box(obj[k]);
}
return result;
}
function unboxify<T>(obj: Boxified<T>): T {
let result = {} as T;
for (let k in obj) {
result[k] = unbox(obj[k]);
}
return result;
}
function assignBoxified<T>(obj: Boxified<T>, values: T) {
for (let k in values) {
obj[k].value = values[k];
}
}
function f1() {
let v = {
a: 42,
b: "hello",
c: true
};
let b = boxify(v);
let x: number = b.a.value;
}
function f2() {
let b = {
a: box(42),
b: box("hello"),
c: box(true)
};
let v = unboxify(b);
let x: number = v.a;
}
function f3() {
let b = {
a: box(42),
b: box("hello"),
c: box(true)
};
assignBoxified(b, { c: false });
}
function f4() {
let b = {
a: box(42),
b: box("hello"),
c: box(true)
};
b = boxify(unboxify(b));
b = unboxify(boxify(b));
}
function makeRecord<T, K extends string>(obj: { [P in K]: T }) {
return obj;
}
function f5(s: string) {
let b = makeRecord({
a: box(42),
b: box("hello"),
c: box(true)
});
let v = unboxify(b);
let x: string | number | boolean = v.a;
}
function makeDictionary<T>(obj: { [x: string]: T }) {
return obj;
}
function f6(s: string) {
let b = makeDictionary({
a: box(42),
b: box("hello"),
c: box(true)
});
let v = unboxify(b);
let x: string | number | boolean = v[s];
}
//// [isomorphicMappedTypeInference.js]
function box(x) {
return { value: x };
}
function unbox(x) {
return x.value;
}
function boxify(obj) {
var result = {};
for (var k in obj) {
result[k] = box(obj[k]);
}
return result;
}
function unboxify(obj) {
var result = {};
for (var k in obj) {
result[k] = unbox(obj[k]);
}
return result;
}
function assignBoxified(obj, values) {
for (var k in values) {
obj[k].value = values[k];
}
}
function f1() {
var v = {
a: 42,
b: "hello",
c: true
};
var b = boxify(v);
var x = b.a.value;
}
function f2() {
var b = {
a: box(42),
b: box("hello"),
c: box(true)
};
var v = unboxify(b);
var x = v.a;
}
function f3() {
var b = {
a: box(42),
b: box("hello"),
c: box(true)
};
assignBoxified(b, { c: false });
}
function f4() {
var b = {
a: box(42),
b: box("hello"),
c: box(true)
};
b = boxify(unboxify(b));
b = unboxify(boxify(b));
}
function makeRecord(obj) {
return obj;
}
function f5(s) {
var b = makeRecord({
a: box(42),
b: box("hello"),
c: box(true)
});
var v = unboxify(b);
var x = v.a;
}
function makeDictionary(obj) {
return obj;
}
function f6(s) {
var b = makeDictionary({
a: box(42),
b: box("hello"),
c: box(true)
});
var v = unboxify(b);
var x = v[s];
}
//// [isomorphicMappedTypeInference.d.ts]
declare type Box<T> = {
value: T;
};
declare type Boxified<T> = {
[P in keyof T]: Box<T[P]>;
};
declare function box<T>(x: T): Box<T>;
declare function unbox<T>(x: Box<T>): T;
declare function boxify<T>(obj: T): Boxified<T>;
declare function unboxify<T>(obj: Boxified<T>): T;
declare function assignBoxified<T>(obj: Boxified<T>, values: T): void;
declare function f1(): void;
declare function f2(): void;
declare function f3(): void;
declare function f4(): void;
declare function makeRecord<T, K extends string>(obj: {
[P in K]: T;
}): {
[P in K]: T;
};
declare function f5(s: string): void;
declare function makeDictionary<T>(obj: {
[x: string]: T;
}): {
[x: string]: T;
};
declare function f6(s: string): void;

View File

@@ -0,0 +1,334 @@
=== tests/cases/conformance/types/mapped/isomorphicMappedTypeInference.ts ===
type Box<T> = {
>Box : Symbol(Box, Decl(isomorphicMappedTypeInference.ts, 0, 0))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 1, 9))
value: T;
>value : Symbol(value, Decl(isomorphicMappedTypeInference.ts, 1, 15))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 1, 9))
}
type Boxified<T> = {
>Boxified : Symbol(Boxified, Decl(isomorphicMappedTypeInference.ts, 3, 1))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 5, 14))
[P in keyof T]: Box<T[P]>;
>P : Symbol(P, Decl(isomorphicMappedTypeInference.ts, 6, 5))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 5, 14))
>Box : Symbol(Box, Decl(isomorphicMappedTypeInference.ts, 0, 0))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 5, 14))
>P : Symbol(P, Decl(isomorphicMappedTypeInference.ts, 6, 5))
}
function box<T>(x: T): Box<T> {
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 9, 13))
>x : Symbol(x, Decl(isomorphicMappedTypeInference.ts, 9, 16))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 9, 13))
>Box : Symbol(Box, Decl(isomorphicMappedTypeInference.ts, 0, 0))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 9, 13))
return { value: x };
>value : Symbol(value, Decl(isomorphicMappedTypeInference.ts, 10, 12))
>x : Symbol(x, Decl(isomorphicMappedTypeInference.ts, 9, 16))
}
function unbox<T>(x: Box<T>): T {
>unbox : Symbol(unbox, Decl(isomorphicMappedTypeInference.ts, 11, 1))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 13, 15))
>x : Symbol(x, Decl(isomorphicMappedTypeInference.ts, 13, 18))
>Box : Symbol(Box, Decl(isomorphicMappedTypeInference.ts, 0, 0))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 13, 15))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 13, 15))
return x.value;
>x.value : Symbol(value, Decl(isomorphicMappedTypeInference.ts, 1, 15))
>x : Symbol(x, Decl(isomorphicMappedTypeInference.ts, 13, 18))
>value : Symbol(value, Decl(isomorphicMappedTypeInference.ts, 1, 15))
}
function boxify<T>(obj: T): Boxified<T> {
>boxify : Symbol(boxify, Decl(isomorphicMappedTypeInference.ts, 15, 1))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 17, 16))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 17, 19))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 17, 16))
>Boxified : Symbol(Boxified, Decl(isomorphicMappedTypeInference.ts, 3, 1))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 17, 16))
let result = {} as Boxified<T>;
>result : Symbol(result, Decl(isomorphicMappedTypeInference.ts, 18, 7))
>Boxified : Symbol(Boxified, Decl(isomorphicMappedTypeInference.ts, 3, 1))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 17, 16))
for (let k in obj) {
>k : Symbol(k, Decl(isomorphicMappedTypeInference.ts, 19, 12))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 17, 19))
result[k] = box(obj[k]);
>result : Symbol(result, Decl(isomorphicMappedTypeInference.ts, 18, 7))
>k : Symbol(k, Decl(isomorphicMappedTypeInference.ts, 19, 12))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 17, 19))
>k : Symbol(k, Decl(isomorphicMappedTypeInference.ts, 19, 12))
}
return result;
>result : Symbol(result, Decl(isomorphicMappedTypeInference.ts, 18, 7))
}
function unboxify<T>(obj: Boxified<T>): T {
>unboxify : Symbol(unboxify, Decl(isomorphicMappedTypeInference.ts, 23, 1))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 25, 18))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 25, 21))
>Boxified : Symbol(Boxified, Decl(isomorphicMappedTypeInference.ts, 3, 1))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 25, 18))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 25, 18))
let result = {} as T;
>result : Symbol(result, Decl(isomorphicMappedTypeInference.ts, 26, 7))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 25, 18))
for (let k in obj) {
>k : Symbol(k, Decl(isomorphicMappedTypeInference.ts, 27, 12))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 25, 21))
result[k] = unbox(obj[k]);
>result : Symbol(result, Decl(isomorphicMappedTypeInference.ts, 26, 7))
>k : Symbol(k, Decl(isomorphicMappedTypeInference.ts, 27, 12))
>unbox : Symbol(unbox, Decl(isomorphicMappedTypeInference.ts, 11, 1))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 25, 21))
>k : Symbol(k, Decl(isomorphicMappedTypeInference.ts, 27, 12))
}
return result;
>result : Symbol(result, Decl(isomorphicMappedTypeInference.ts, 26, 7))
}
function assignBoxified<T>(obj: Boxified<T>, values: T) {
>assignBoxified : Symbol(assignBoxified, Decl(isomorphicMappedTypeInference.ts, 31, 1))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 33, 24))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 33, 27))
>Boxified : Symbol(Boxified, Decl(isomorphicMappedTypeInference.ts, 3, 1))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 33, 24))
>values : Symbol(values, Decl(isomorphicMappedTypeInference.ts, 33, 44))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 33, 24))
for (let k in values) {
>k : Symbol(k, Decl(isomorphicMappedTypeInference.ts, 34, 12))
>values : Symbol(values, Decl(isomorphicMappedTypeInference.ts, 33, 44))
obj[k].value = values[k];
>obj[k].value : Symbol(value, Decl(isomorphicMappedTypeInference.ts, 1, 15))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 33, 27))
>k : Symbol(k, Decl(isomorphicMappedTypeInference.ts, 34, 12))
>value : Symbol(value, Decl(isomorphicMappedTypeInference.ts, 1, 15))
>values : Symbol(values, Decl(isomorphicMappedTypeInference.ts, 33, 44))
>k : Symbol(k, Decl(isomorphicMappedTypeInference.ts, 34, 12))
}
}
function f1() {
>f1 : Symbol(f1, Decl(isomorphicMappedTypeInference.ts, 37, 1))
let v = {
>v : Symbol(v, Decl(isomorphicMappedTypeInference.ts, 40, 7))
a: 42,
>a : Symbol(a, Decl(isomorphicMappedTypeInference.ts, 40, 13))
b: "hello",
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 41, 14))
c: true
>c : Symbol(c, Decl(isomorphicMappedTypeInference.ts, 42, 19))
};
let b = boxify(v);
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 45, 7))
>boxify : Symbol(boxify, Decl(isomorphicMappedTypeInference.ts, 15, 1))
>v : Symbol(v, Decl(isomorphicMappedTypeInference.ts, 40, 7))
let x: number = b.a.value;
>x : Symbol(x, Decl(isomorphicMappedTypeInference.ts, 46, 7))
>b.a.value : Symbol(value, Decl(isomorphicMappedTypeInference.ts, 1, 15))
>b.a : Symbol(a)
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 45, 7))
>a : Symbol(a)
>value : Symbol(value, Decl(isomorphicMappedTypeInference.ts, 1, 15))
}
function f2() {
>f2 : Symbol(f2, Decl(isomorphicMappedTypeInference.ts, 47, 1))
let b = {
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 50, 7))
a: box(42),
>a : Symbol(a, Decl(isomorphicMappedTypeInference.ts, 50, 13))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
b: box("hello"),
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 51, 19))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
c: box(true)
>c : Symbol(c, Decl(isomorphicMappedTypeInference.ts, 52, 24))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
};
let v = unboxify(b);
>v : Symbol(v, Decl(isomorphicMappedTypeInference.ts, 55, 7))
>unboxify : Symbol(unboxify, Decl(isomorphicMappedTypeInference.ts, 23, 1))
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 50, 7))
let x: number = v.a;
>x : Symbol(x, Decl(isomorphicMappedTypeInference.ts, 56, 7))
>v.a : Symbol(a, Decl(isomorphicMappedTypeInference.ts, 50, 13))
>v : Symbol(v, Decl(isomorphicMappedTypeInference.ts, 55, 7))
>a : Symbol(a, Decl(isomorphicMappedTypeInference.ts, 50, 13))
}
function f3() {
>f3 : Symbol(f3, Decl(isomorphicMappedTypeInference.ts, 57, 1))
let b = {
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 60, 7))
a: box(42),
>a : Symbol(a, Decl(isomorphicMappedTypeInference.ts, 60, 13))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
b: box("hello"),
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 61, 19))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
c: box(true)
>c : Symbol(c, Decl(isomorphicMappedTypeInference.ts, 62, 24))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
};
assignBoxified(b, { c: false });
>assignBoxified : Symbol(assignBoxified, Decl(isomorphicMappedTypeInference.ts, 31, 1))
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 60, 7))
>c : Symbol(c, Decl(isomorphicMappedTypeInference.ts, 65, 23))
}
function f4() {
>f4 : Symbol(f4, Decl(isomorphicMappedTypeInference.ts, 66, 1))
let b = {
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 69, 7))
a: box(42),
>a : Symbol(a, Decl(isomorphicMappedTypeInference.ts, 69, 13))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
b: box("hello"),
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 70, 19))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
c: box(true)
>c : Symbol(c, Decl(isomorphicMappedTypeInference.ts, 71, 24))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
};
b = boxify(unboxify(b));
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 69, 7))
>boxify : Symbol(boxify, Decl(isomorphicMappedTypeInference.ts, 15, 1))
>unboxify : Symbol(unboxify, Decl(isomorphicMappedTypeInference.ts, 23, 1))
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 69, 7))
b = unboxify(boxify(b));
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 69, 7))
>unboxify : Symbol(unboxify, Decl(isomorphicMappedTypeInference.ts, 23, 1))
>boxify : Symbol(boxify, Decl(isomorphicMappedTypeInference.ts, 15, 1))
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 69, 7))
}
function makeRecord<T, K extends string>(obj: { [P in K]: T }) {
>makeRecord : Symbol(makeRecord, Decl(isomorphicMappedTypeInference.ts, 76, 1))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 78, 20))
>K : Symbol(K, Decl(isomorphicMappedTypeInference.ts, 78, 22))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 78, 41))
>P : Symbol(P, Decl(isomorphicMappedTypeInference.ts, 78, 49))
>K : Symbol(K, Decl(isomorphicMappedTypeInference.ts, 78, 22))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 78, 20))
return obj;
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 78, 41))
}
function f5(s: string) {
>f5 : Symbol(f5, Decl(isomorphicMappedTypeInference.ts, 80, 1))
>s : Symbol(s, Decl(isomorphicMappedTypeInference.ts, 82, 12))
let b = makeRecord({
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 83, 7))
>makeRecord : Symbol(makeRecord, Decl(isomorphicMappedTypeInference.ts, 76, 1))
a: box(42),
>a : Symbol(a, Decl(isomorphicMappedTypeInference.ts, 83, 24))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
b: box("hello"),
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 84, 19))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
c: box(true)
>c : Symbol(c, Decl(isomorphicMappedTypeInference.ts, 85, 24))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
});
let v = unboxify(b);
>v : Symbol(v, Decl(isomorphicMappedTypeInference.ts, 88, 7))
>unboxify : Symbol(unboxify, Decl(isomorphicMappedTypeInference.ts, 23, 1))
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 83, 7))
let x: string | number | boolean = v.a;
>x : Symbol(x, Decl(isomorphicMappedTypeInference.ts, 89, 7))
>v.a : Symbol(a)
>v : Symbol(v, Decl(isomorphicMappedTypeInference.ts, 88, 7))
>a : Symbol(a)
}
function makeDictionary<T>(obj: { [x: string]: T }) {
>makeDictionary : Symbol(makeDictionary, Decl(isomorphicMappedTypeInference.ts, 90, 1))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 92, 24))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 92, 27))
>x : Symbol(x, Decl(isomorphicMappedTypeInference.ts, 92, 35))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 92, 24))
return obj;
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 92, 27))
}
function f6(s: string) {
>f6 : Symbol(f6, Decl(isomorphicMappedTypeInference.ts, 94, 1))
>s : Symbol(s, Decl(isomorphicMappedTypeInference.ts, 96, 12))
let b = makeDictionary({
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 97, 7))
>makeDictionary : Symbol(makeDictionary, Decl(isomorphicMappedTypeInference.ts, 90, 1))
a: box(42),
>a : Symbol(a, Decl(isomorphicMappedTypeInference.ts, 97, 28))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
b: box("hello"),
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 98, 19))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
c: box(true)
>c : Symbol(c, Decl(isomorphicMappedTypeInference.ts, 99, 24))
>box : Symbol(box, Decl(isomorphicMappedTypeInference.ts, 7, 1))
});
let v = unboxify(b);
>v : Symbol(v, Decl(isomorphicMappedTypeInference.ts, 102, 7))
>unboxify : Symbol(unboxify, Decl(isomorphicMappedTypeInference.ts, 23, 1))
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 97, 7))
let x: string | number | boolean = v[s];
>x : Symbol(x, Decl(isomorphicMappedTypeInference.ts, 103, 7))
>v : Symbol(v, Decl(isomorphicMappedTypeInference.ts, 102, 7))
>s : Symbol(s, Decl(isomorphicMappedTypeInference.ts, 96, 12))
}

View File

@@ -0,0 +1,405 @@
=== tests/cases/conformance/types/mapped/isomorphicMappedTypeInference.ts ===
type Box<T> = {
>Box : Box<T>
>T : T
value: T;
>value : T
>T : T
}
type Boxified<T> = {
>Boxified : Boxified<T>
>T : T
[P in keyof T]: Box<T[P]>;
>P : P
>T : T
>Box : Box<T>
>T : T
>P : P
}
function box<T>(x: T): Box<T> {
>box : <T>(x: T) => Box<T>
>T : T
>x : T
>T : T
>Box : Box<T>
>T : T
return { value: x };
>{ value: x } : { value: T; }
>value : T
>x : T
}
function unbox<T>(x: Box<T>): T {
>unbox : <T>(x: Box<T>) => T
>T : T
>x : Box<T>
>Box : Box<T>
>T : T
>T : T
return x.value;
>x.value : T
>x : Box<T>
>value : T
}
function boxify<T>(obj: T): Boxified<T> {
>boxify : <T>(obj: T) => Boxified<T>
>T : T
>obj : T
>T : T
>Boxified : Boxified<T>
>T : T
let result = {} as Boxified<T>;
>result : Boxified<T>
>{} as Boxified<T> : Boxified<T>
>{} : {}
>Boxified : Boxified<T>
>T : T
for (let k in obj) {
>k : keyof T
>obj : T
result[k] = box(obj[k]);
>result[k] = box(obj[k]) : Box<T[keyof T]>
>result[k] : Box<T[keyof T]>
>result : Boxified<T>
>k : keyof T
>box(obj[k]) : Box<T[keyof T]>
>box : <T>(x: T) => Box<T>
>obj[k] : T[keyof T]
>obj : T
>k : keyof T
}
return result;
>result : Boxified<T>
}
function unboxify<T>(obj: Boxified<T>): T {
>unboxify : <T>(obj: Boxified<T>) => T
>T : T
>obj : Boxified<T>
>Boxified : Boxified<T>
>T : T
>T : T
let result = {} as T;
>result : T
>{} as T : T
>{} : {}
>T : T
for (let k in obj) {
>k : keyof T
>obj : Boxified<T>
result[k] = unbox(obj[k]);
>result[k] = unbox(obj[k]) : T[keyof T]
>result[k] : T[keyof T]
>result : T
>k : keyof T
>unbox(obj[k]) : T[keyof T]
>unbox : <T>(x: Box<T>) => T
>obj[k] : Box<T[keyof T]>
>obj : Boxified<T>
>k : keyof T
}
return result;
>result : T
}
function assignBoxified<T>(obj: Boxified<T>, values: T) {
>assignBoxified : <T>(obj: Boxified<T>, values: T) => void
>T : T
>obj : Boxified<T>
>Boxified : Boxified<T>
>T : T
>values : T
>T : T
for (let k in values) {
>k : keyof T
>values : T
obj[k].value = values[k];
>obj[k].value = values[k] : T[keyof T]
>obj[k].value : T[keyof T]
>obj[k] : Box<T[keyof T]>
>obj : Boxified<T>
>k : keyof T
>value : T[keyof T]
>values[k] : T[keyof T]
>values : T
>k : keyof T
}
}
function f1() {
>f1 : () => void
let v = {
>v : { a: number; b: string; c: boolean; }
>{ a: 42, b: "hello", c: true } : { a: number; b: string; c: boolean; }
a: 42,
>a : number
>42 : 42
b: "hello",
>b : string
>"hello" : "hello"
c: true
>c : boolean
>true : true
};
let b = boxify(v);
>b : Boxified<{ a: number; b: string; c: boolean; }>
>boxify(v) : Boxified<{ a: number; b: string; c: boolean; }>
>boxify : <T>(obj: T) => Boxified<T>
>v : { a: number; b: string; c: boolean; }
let x: number = b.a.value;
>x : number
>b.a.value : number
>b.a : Box<number>
>b : Boxified<{ a: number; b: string; c: boolean; }>
>a : Box<number>
>value : number
}
function f2() {
>f2 : () => void
let b = {
>b : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
>{ a: box(42), b: box("hello"), c: box(true) } : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
a: box(42),
>a : Box<number>
>box(42) : Box<number>
>box : <T>(x: T) => Box<T>
>42 : 42
b: box("hello"),
>b : Box<string>
>box("hello") : Box<string>
>box : <T>(x: T) => Box<T>
>"hello" : "hello"
c: box(true)
>c : Box<boolean>
>box(true) : Box<boolean>
>box : <T>(x: T) => Box<T>
>true : true
};
let v = unboxify(b);
>v : { a: number; b: string; c: boolean; }
>unboxify(b) : { a: number; b: string; c: boolean; }
>unboxify : <T>(obj: Boxified<T>) => T
>b : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
let x: number = v.a;
>x : number
>v.a : number
>v : { a: number; b: string; c: boolean; }
>a : number
}
function f3() {
>f3 : () => void
let b = {
>b : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
>{ a: box(42), b: box("hello"), c: box(true) } : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
a: box(42),
>a : Box<number>
>box(42) : Box<number>
>box : <T>(x: T) => Box<T>
>42 : 42
b: box("hello"),
>b : Box<string>
>box("hello") : Box<string>
>box : <T>(x: T) => Box<T>
>"hello" : "hello"
c: box(true)
>c : Box<boolean>
>box(true) : Box<boolean>
>box : <T>(x: T) => Box<T>
>true : true
};
assignBoxified(b, { c: false });
>assignBoxified(b, { c: false }) : void
>assignBoxified : <T>(obj: Boxified<T>, values: T) => void
>b : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
>{ c: false } : { c: false; }
>c : boolean
>false : false
}
function f4() {
>f4 : () => void
let b = {
>b : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
>{ a: box(42), b: box("hello"), c: box(true) } : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
a: box(42),
>a : Box<number>
>box(42) : Box<number>
>box : <T>(x: T) => Box<T>
>42 : 42
b: box("hello"),
>b : Box<string>
>box("hello") : Box<string>
>box : <T>(x: T) => Box<T>
>"hello" : "hello"
c: box(true)
>c : Box<boolean>
>box(true) : Box<boolean>
>box : <T>(x: T) => Box<T>
>true : true
};
b = boxify(unboxify(b));
>b = boxify(unboxify(b)) : Boxified<{ a: number; b: string; c: boolean; }>
>b : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
>boxify(unboxify(b)) : Boxified<{ a: number; b: string; c: boolean; }>
>boxify : <T>(obj: T) => Boxified<T>
>unboxify(b) : { a: number; b: string; c: boolean; }
>unboxify : <T>(obj: Boxified<T>) => T
>b : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
b = unboxify(boxify(b));
>b = unboxify(boxify(b)) : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
>b : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
>unboxify(boxify(b)) : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
>unboxify : <T>(obj: Boxified<T>) => T
>boxify(b) : Boxified<{ a: Box<number>; b: Box<string>; c: Box<boolean>; }>
>boxify : <T>(obj: T) => Boxified<T>
>b : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
}
function makeRecord<T, K extends string>(obj: { [P in K]: T }) {
>makeRecord : <T, K extends string>(obj: { [P in K]: T; }) => { [P in K]: T; }
>T : T
>K : K
>obj : { [P in K]: T; }
>P : P
>K : K
>T : T
return obj;
>obj : { [P in K]: T; }
}
function f5(s: string) {
>f5 : (s: string) => void
>s : string
let b = makeRecord({
>b : { a: Box<number> | Box<string> | Box<boolean>; b: Box<number> | Box<string> | Box<boolean>; c: Box<number> | Box<string> | Box<boolean>; }
>makeRecord({ a: box(42), b: box("hello"), c: box(true) }) : { a: Box<number> | Box<string> | Box<boolean>; b: Box<number> | Box<string> | Box<boolean>; c: Box<number> | Box<string> | Box<boolean>; }
>makeRecord : <T, K extends string>(obj: { [P in K]: T; }) => { [P in K]: T; }
>{ a: box(42), b: box("hello"), c: box(true) } : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
a: box(42),
>a : Box<number>
>box(42) : Box<number>
>box : <T>(x: T) => Box<T>
>42 : 42
b: box("hello"),
>b : Box<string>
>box("hello") : Box<string>
>box : <T>(x: T) => Box<T>
>"hello" : "hello"
c: box(true)
>c : Box<boolean>
>box(true) : Box<boolean>
>box : <T>(x: T) => Box<T>
>true : true
});
let v = unboxify(b);
>v : { a: string | number | boolean; b: string | number | boolean; c: string | number | boolean; }
>unboxify(b) : { a: string | number | boolean; b: string | number | boolean; c: string | number | boolean; }
>unboxify : <T>(obj: Boxified<T>) => T
>b : { a: Box<number> | Box<string> | Box<boolean>; b: Box<number> | Box<string> | Box<boolean>; c: Box<number> | Box<string> | Box<boolean>; }
let x: string | number | boolean = v.a;
>x : string | number | boolean
>v.a : string | number | boolean
>v : { a: string | number | boolean; b: string | number | boolean; c: string | number | boolean; }
>a : string | number | boolean
}
function makeDictionary<T>(obj: { [x: string]: T }) {
>makeDictionary : <T>(obj: { [x: string]: T; }) => { [x: string]: T; }
>T : T
>obj : { [x: string]: T; }
>x : string
>T : T
return obj;
>obj : { [x: string]: T; }
}
function f6(s: string) {
>f6 : (s: string) => void
>s : string
let b = makeDictionary({
>b : { [x: string]: Box<number> | Box<string> | Box<boolean>; }
>makeDictionary({ a: box(42), b: box("hello"), c: box(true) }) : { [x: string]: Box<number> | Box<string> | Box<boolean>; }
>makeDictionary : <T>(obj: { [x: string]: T; }) => { [x: string]: T; }
>{ a: box(42), b: box("hello"), c: box(true) } : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
a: box(42),
>a : Box<number>
>box(42) : Box<number>
>box : <T>(x: T) => Box<T>
>42 : 42
b: box("hello"),
>b : Box<string>
>box("hello") : Box<string>
>box : <T>(x: T) => Box<T>
>"hello" : "hello"
c: box(true)
>c : Box<boolean>
>box(true) : Box<boolean>
>box : <T>(x: T) => Box<T>
>true : true
});
let v = unboxify(b);
>v : { [x: string]: string | number | boolean; }
>unboxify(b) : { [x: string]: string | number | boolean; }
>unboxify : <T>(obj: Boxified<T>) => T
>b : { [x: string]: Box<number> | Box<string> | Box<boolean>; }
let x: string | number | boolean = v[s];
>x : string | number | boolean
>v[s] : string | number | boolean
>v : { [x: string]: string | number | boolean; }
>s : string
}

View File

@@ -0,0 +1,107 @@
// @noimplicitany: true
// @declaration: true
type Box<T> = {
value: T;
}
type Boxified<T> = {
[P in keyof T]: Box<T[P]>;
}
function box<T>(x: T): Box<T> {
return { value: x };
}
function unbox<T>(x: Box<T>): T {
return x.value;
}
function boxify<T>(obj: T): Boxified<T> {
let result = {} as Boxified<T>;
for (let k in obj) {
result[k] = box(obj[k]);
}
return result;
}
function unboxify<T>(obj: Boxified<T>): T {
let result = {} as T;
for (let k in obj) {
result[k] = unbox(obj[k]);
}
return result;
}
function assignBoxified<T>(obj: Boxified<T>, values: T) {
for (let k in values) {
obj[k].value = values[k];
}
}
function f1() {
let v = {
a: 42,
b: "hello",
c: true
};
let b = boxify(v);
let x: number = b.a.value;
}
function f2() {
let b = {
a: box(42),
b: box("hello"),
c: box(true)
};
let v = unboxify(b);
let x: number = v.a;
}
function f3() {
let b = {
a: box(42),
b: box("hello"),
c: box(true)
};
assignBoxified(b, { c: false });
}
function f4() {
let b = {
a: box(42),
b: box("hello"),
c: box(true)
};
b = boxify(unboxify(b));
b = unboxify(boxify(b));
}
function makeRecord<T, K extends string>(obj: { [P in K]: T }) {
return obj;
}
function f5(s: string) {
let b = makeRecord({
a: box(42),
b: box("hello"),
c: box(true)
});
let v = unboxify(b);
let x: string | number | boolean = v.a;
}
function makeDictionary<T>(obj: { [x: string]: T }) {
return obj;
}
function f6(s: string) {
let b = makeDictionary({
a: box(42),
b: box("hello"),
c: box(true)
});
let v = unboxify(b);
let x: string | number | boolean = v[s];
}