Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infer intersected reverse mapped types #52062

Closed
31 changes: 22 additions & 9 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16738,7 +16738,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const type = elementTypes[i];
const flags = target.elementFlags[i];
if (flags & ElementFlags.Variadic) {
if (type.flags & TypeFlags.InstantiableNonPrimitive || isGenericMappedType(type)) {
if (type.flags & TypeFlags.InstantiableNonPrimitive || everyContainedType(type, isGenericMappedType)) {
// Generic variadic elements stay as they are.
addElement(type, ElementFlags.Variadic, target.labeledElementDeclarations?.[i]);
}
Expand Down Expand Up @@ -30246,13 +30246,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {

function getTypeOfPropertyOfContextualType(type: Type, name: __String, nameType?: Type) {
return mapType(type, t => {
if (isGenericMappedType(t) && !t.declaration.nameType) {
const constraint = getConstraintTypeFromMappedType(t);
const constraintOfConstraint = getBaseConstraintOfType(constraint) || constraint;
const propertyNameType = nameType || getStringLiteralType(unescapeLeadingUnderscores(name));
if (isTypeAssignableTo(propertyNameType, constraintOfConstraint)) {
return substituteIndexedMappedType(t, propertyNameType);
}
if (everyContainedType(t, t => isGenericMappedType(t) && !t.declaration.nameType)) {
const newTypes = mapDefined(t.flags & TypeFlags.Intersection ? (t as IntersectionType).types : [t], t => {
const mappedType = t as MappedType;
const constraint = getConstraintTypeFromMappedType(mappedType);
const constraintOfConstraint = getBaseConstraintOfType(constraint) || constraint;
const propertyNameType = nameType || getStringLiteralType(unescapeLeadingUnderscores(name));
if (isTypeAssignableTo(propertyNameType, constraintOfConstraint)) {
return substituteIndexedMappedType(mappedType, propertyNameType);
}
});
return newTypes.length ? getIntersectionType(newTypes) : undefined;
}
else if (t.flags & TypeFlags.StructuredType) {
const prop = getPropertyOfType(t, name);
Expand Down Expand Up @@ -30486,6 +30490,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
);
}

function getApparentTypeOfInstantiatedContextualType(type: Type) {
return getObjectFlags(type) & ObjectFlags.Mapped ? type : getApparentType(type);
}

// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
// be "pushed" onto a node using the contextualType property.
function getApparentTypeOfContextualType(node: Expression | MethodDeclaration, contextFlags: ContextFlags | undefined): Type | undefined {
Expand All @@ -30500,7 +30508,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// That would evaluate mapped types with array or tuple type constraints too eagerly
// and thus it would prevent `getTypeOfPropertyOfContextualType` from obtaining per-position contextual type for elements of array literal expressions.
// Apparent type of other mapped types is already the mapped type itself so we can just avoid calling `getApparentType` here for all mapped types.
t => getObjectFlags(t) & ObjectFlags.Mapped ? t : getApparentType(t),
t => {
if (t.flags & TypeFlags.Intersection) {
return getIntersectionType(map((t as IntersectionType).types, getApparentTypeOfInstantiatedContextualType));
}
return getApparentTypeOfInstantiatedContextualType(t);
},
/*noReductions*/ true,
);
return apparentType.flags & TypeFlags.Union && isObjectLiteralExpression(node) ? discriminateContextualTypeByObjectMembers(node, apparentType as UnionType) :
Expand Down
266 changes: 266 additions & 0 deletions tests/baselines/reference/reverseMappedIntersectionInference.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
//// [tests/cases/compiler/reverseMappedIntersectionInference.ts] ////

=== reverseMappedIntersectionInference.ts ===
type Results<T> = {
>Results : Symbol(Results, Decl(reverseMappedIntersectionInference.ts, 0, 0))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 0, 13))

[K in keyof T]: {
>K : Symbol(K, Decl(reverseMappedIntersectionInference.ts, 1, 3))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 0, 13))

data: T[K];
>data : Symbol(data, Decl(reverseMappedIntersectionInference.ts, 1, 19))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 0, 13))
>K : Symbol(K, Decl(reverseMappedIntersectionInference.ts, 1, 3))

onSuccess: (data: T[K]) => void;
>onSuccess : Symbol(onSuccess, Decl(reverseMappedIntersectionInference.ts, 2, 15))
>data : Symbol(data, Decl(reverseMappedIntersectionInference.ts, 3, 16))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 0, 13))
>K : Symbol(K, Decl(reverseMappedIntersectionInference.ts, 1, 3))

};
};

type Errors<E> = {
>Errors : Symbol(Errors, Decl(reverseMappedIntersectionInference.ts, 5, 2))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 7, 12))

[K in keyof E]: {
>K : Symbol(K, Decl(reverseMappedIntersectionInference.ts, 8, 3))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 7, 12))

error: E[K];
>error : Symbol(error, Decl(reverseMappedIntersectionInference.ts, 8, 19))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 7, 12))
>K : Symbol(K, Decl(reverseMappedIntersectionInference.ts, 8, 3))

onError: (data: E[K]) => void;
>onError : Symbol(onError, Decl(reverseMappedIntersectionInference.ts, 9, 16))
>data : Symbol(data, Decl(reverseMappedIntersectionInference.ts, 10, 14))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 7, 12))
>K : Symbol(K, Decl(reverseMappedIntersectionInference.ts, 8, 3))

};
};

declare function withKeyedObj<T, E>(
>withKeyedObj : Symbol(withKeyedObj, Decl(reverseMappedIntersectionInference.ts, 12, 2))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 14, 30))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 14, 32))

arg: Results<T> & Errors<E>
>arg : Symbol(arg, Decl(reverseMappedIntersectionInference.ts, 14, 36))
>Results : Symbol(Results, Decl(reverseMappedIntersectionInference.ts, 0, 0))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 14, 30))
>Errors : Symbol(Errors, Decl(reverseMappedIntersectionInference.ts, 5, 2))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 14, 32))

): [T, E];
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 14, 30))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 14, 32))

const res = withKeyedObj({
>res : Symbol(res, Decl(reverseMappedIntersectionInference.ts, 18, 5))
>withKeyedObj : Symbol(withKeyedObj, Decl(reverseMappedIntersectionInference.ts, 12, 2))

a: {
>a : Symbol(a, Decl(reverseMappedIntersectionInference.ts, 18, 26))

data: "foo",
>data : Symbol(data, Decl(reverseMappedIntersectionInference.ts, 19, 6))

onSuccess: (dataArg) => {
>onSuccess : Symbol(onSuccess, Decl(reverseMappedIntersectionInference.ts, 20, 16))
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 21, 16))

dataArg;
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 21, 16))

},
error: 404,
>error : Symbol(error, Decl(reverseMappedIntersectionInference.ts, 23, 6))

onError: (errorArg) => {
>onError : Symbol(onError, Decl(reverseMappedIntersectionInference.ts, 24, 15))
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 25, 14))

errorArg;
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 25, 14))

},
},
b: {
>b : Symbol(b, Decl(reverseMappedIntersectionInference.ts, 28, 4))

data: true,
>data : Symbol(data, Decl(reverseMappedIntersectionInference.ts, 29, 6))

onSuccess: (dataArg) => {
>onSuccess : Symbol(onSuccess, Decl(reverseMappedIntersectionInference.ts, 30, 15))
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 31, 16))

dataArg;
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 31, 16))

},
error: 500,
>error : Symbol(error, Decl(reverseMappedIntersectionInference.ts, 33, 6))

onError: (errorArg) => {
>onError : Symbol(onError, Decl(reverseMappedIntersectionInference.ts, 34, 15))
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 35, 14))

errorArg;
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 35, 14))

},
},
});

declare function withTuples<T extends any[], E extends any[]>(
>withTuples : Symbol(withTuples, Decl(reverseMappedIntersectionInference.ts, 39, 3))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 41, 28))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 41, 44))

arg: [...(Results<T> & Errors<E>)]
>arg : Symbol(arg, Decl(reverseMappedIntersectionInference.ts, 41, 62))
>Results : Symbol(Results, Decl(reverseMappedIntersectionInference.ts, 0, 0))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 41, 28))
>Errors : Symbol(Errors, Decl(reverseMappedIntersectionInference.ts, 5, 2))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 41, 44))

): [T, E];
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 41, 28))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 41, 44))

const res2 = withTuples([
>res2 : Symbol(res2, Decl(reverseMappedIntersectionInference.ts, 45, 5))
>withTuples : Symbol(withTuples, Decl(reverseMappedIntersectionInference.ts, 39, 3))
{
data: "foo",
>data : Symbol(data, Decl(reverseMappedIntersectionInference.ts, 46, 3))

onSuccess: (dataArg) => {
>onSuccess : Symbol(onSuccess, Decl(reverseMappedIntersectionInference.ts, 47, 16))
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 48, 16))

dataArg;
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 48, 16))

},
error: 404,
>error : Symbol(error, Decl(reverseMappedIntersectionInference.ts, 50, 6))

onError: (errorArg) => {
>onError : Symbol(onError, Decl(reverseMappedIntersectionInference.ts, 51, 15))
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 52, 14))

errorArg;
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 52, 14))

},
},
{
data: true,
>data : Symbol(data, Decl(reverseMappedIntersectionInference.ts, 56, 3))

onSuccess: (dataArg) => {
>onSuccess : Symbol(onSuccess, Decl(reverseMappedIntersectionInference.ts, 57, 15))
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 58, 16))

dataArg;
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 58, 16))

},
error: 500,
>error : Symbol(error, Decl(reverseMappedIntersectionInference.ts, 60, 6))

onError: (errorArg) => {
>onError : Symbol(onError, Decl(reverseMappedIntersectionInference.ts, 61, 15))
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 62, 14))

errorArg;
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 62, 14))

},
},
]);

type Tuple<T> = readonly [T, ...T[]];
>Tuple : Symbol(Tuple, Decl(reverseMappedIntersectionInference.ts, 66, 3))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 68, 11))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 68, 11))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 68, 11))

declare function withTuplesConstraints<T extends Tuple<any>, E extends Tuple<any>>(
>withTuplesConstraints : Symbol(withTuplesConstraints, Decl(reverseMappedIntersectionInference.ts, 68, 37))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 70, 39))
>Tuple : Symbol(Tuple, Decl(reverseMappedIntersectionInference.ts, 66, 3))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 70, 60))
>Tuple : Symbol(Tuple, Decl(reverseMappedIntersectionInference.ts, 66, 3))

arg: Results<T> & Errors<E>
>arg : Symbol(arg, Decl(reverseMappedIntersectionInference.ts, 70, 83))
>Results : Symbol(Results, Decl(reverseMappedIntersectionInference.ts, 0, 0))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 70, 39))
>Errors : Symbol(Errors, Decl(reverseMappedIntersectionInference.ts, 5, 2))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 70, 60))

): [T, E];
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 70, 39))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 70, 60))

const res3 = withTuplesConstraints([
>res3 : Symbol(res3, Decl(reverseMappedIntersectionInference.ts, 74, 5))
>withTuplesConstraints : Symbol(withTuplesConstraints, Decl(reverseMappedIntersectionInference.ts, 68, 37))
{
data: "foo",
>data : Symbol(data, Decl(reverseMappedIntersectionInference.ts, 75, 3))

onSuccess: (dataArg) => {
>onSuccess : Symbol(onSuccess, Decl(reverseMappedIntersectionInference.ts, 76, 16))
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 77, 16))

dataArg;
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 77, 16))

},
error: 404,
>error : Symbol(error, Decl(reverseMappedIntersectionInference.ts, 79, 6))

onError: (errorArg) => {
>onError : Symbol(onError, Decl(reverseMappedIntersectionInference.ts, 80, 15))
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 81, 14))

errorArg;
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 81, 14))

},
},
{
data: true,
>data : Symbol(data, Decl(reverseMappedIntersectionInference.ts, 85, 3))

onSuccess: (dataArg) => {
>onSuccess : Symbol(onSuccess, Decl(reverseMappedIntersectionInference.ts, 86, 15))
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 87, 16))

dataArg;
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 87, 16))

},
error: 500,
>error : Symbol(error, Decl(reverseMappedIntersectionInference.ts, 89, 6))

onError: (errorArg) => {
>onError : Symbol(onError, Decl(reverseMappedIntersectionInference.ts, 90, 15))
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 91, 14))

errorArg;
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 91, 14))

},
},
]);
Loading
Loading