diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cd89bbf688a53..ae4f5fee3b907 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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]); } @@ -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); @@ -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 { @@ -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) : diff --git a/tests/baselines/reference/reverseMappedIntersectionInference.symbols b/tests/baselines/reference/reverseMappedIntersectionInference.symbols new file mode 100644 index 0000000000000..855d5a483c66d --- /dev/null +++ b/tests/baselines/reference/reverseMappedIntersectionInference.symbols @@ -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)) + + }, + }, +]); diff --git a/tests/baselines/reference/reverseMappedIntersectionInference.types b/tests/baselines/reference/reverseMappedIntersectionInference.types new file mode 100644 index 0000000000000..b1a08efc5f4b4 --- /dev/null +++ b/tests/baselines/reference/reverseMappedIntersectionInference.types @@ -0,0 +1,261 @@ +//// [tests/cases/compiler/reverseMappedIntersectionInference.ts] //// + +=== reverseMappedIntersectionInference.ts === +type Results<T> = { +>Results : Results<T> + + [K in keyof T]: { + data: T[K]; +>data : T[K] + + onSuccess: (data: T[K]) => void; +>onSuccess : (data: T[K]) => void +>data : T[K] + + }; +}; + +type Errors<E> = { +>Errors : Errors<E> + + [K in keyof E]: { + error: E[K]; +>error : E[K] + + onError: (data: E[K]) => void; +>onError : (data: E[K]) => void +>data : E[K] + + }; +}; + +declare function withKeyedObj<T, E>( +>withKeyedObj : <T, E>(arg: Results<T> & Errors<E>) => [T, E] + + arg: Results<T> & Errors<E> +>arg : Results<T> & Errors<E> + +): [T, E]; + +const res = withKeyedObj({ +>res : [{ a: string; b: boolean; }, { a: number; b: number; }] +>withKeyedObj({ a: { data: "foo", onSuccess: (dataArg) => { dataArg; }, error: 404, onError: (errorArg) => { errorArg; }, }, b: { data: true, onSuccess: (dataArg) => { dataArg; }, error: 500, onError: (errorArg) => { errorArg; }, },}) : [{ a: string; b: boolean; }, { a: number; b: number; }] +>withKeyedObj : <T, E>(arg: Results<T> & Errors<E>) => [T, E] +>{ a: { data: "foo", onSuccess: (dataArg) => { dataArg; }, error: 404, onError: (errorArg) => { errorArg; }, }, b: { data: true, onSuccess: (dataArg) => { dataArg; }, error: 500, onError: (errorArg) => { errorArg; }, },} : { a: { data: string; onSuccess: (dataArg: string) => void; error: number; onError: (errorArg: number) => void; }; b: { data: true; onSuccess: (dataArg: boolean) => void; error: number; onError: (errorArg: number) => void; }; } + + a: { +>a : { data: string; onSuccess: (dataArg: string) => void; error: number; onError: (errorArg: number) => void; } +>{ data: "foo", onSuccess: (dataArg) => { dataArg; }, error: 404, onError: (errorArg) => { errorArg; }, } : { data: string; onSuccess: (dataArg: string) => void; error: number; onError: (errorArg: number) => void; } + + data: "foo", +>data : string +>"foo" : "foo" + + onSuccess: (dataArg) => { +>onSuccess : (dataArg: string) => void +>(dataArg) => { dataArg; } : (dataArg: string) => void +>dataArg : string + + dataArg; +>dataArg : string + + }, + error: 404, +>error : number +>404 : 404 + + onError: (errorArg) => { +>onError : (errorArg: number) => void +>(errorArg) => { errorArg; } : (errorArg: number) => void +>errorArg : number + + errorArg; +>errorArg : number + + }, + }, + b: { +>b : { data: true; onSuccess: (dataArg: boolean) => void; error: number; onError: (errorArg: number) => void; } +>{ data: true, onSuccess: (dataArg) => { dataArg; }, error: 500, onError: (errorArg) => { errorArg; }, } : { data: true; onSuccess: (dataArg: boolean) => void; error: number; onError: (errorArg: number) => void; } + + data: true, +>data : true +>true : true + + onSuccess: (dataArg) => { +>onSuccess : (dataArg: boolean) => void +>(dataArg) => { dataArg; } : (dataArg: boolean) => void +>dataArg : boolean + + dataArg; +>dataArg : boolean + + }, + error: 500, +>error : number +>500 : 500 + + onError: (errorArg) => { +>onError : (errorArg: number) => void +>(errorArg) => { errorArg; } : (errorArg: number) => void +>errorArg : number + + errorArg; +>errorArg : number + + }, + }, +}); + +declare function withTuples<T extends any[], E extends any[]>( +>withTuples : <T extends any[], E extends any[]>(arg: [...(Results<T> & Errors<E>)]) => [T, E] + + arg: [...(Results<T> & Errors<E>)] +>arg : [...Results<T> & Errors<E>] + +): [T, E]; + +const res2 = withTuples([ +>res2 : [[string, boolean], [number, number]] +>withTuples([ { data: "foo", onSuccess: (dataArg) => { dataArg; }, error: 404, onError: (errorArg) => { errorArg; }, }, { data: true, onSuccess: (dataArg) => { dataArg; }, error: 500, onError: (errorArg) => { errorArg; }, },]) : [[string, boolean], [number, number]] +>withTuples : <T extends any[], E extends any[]>(arg: [...Results<T> & Errors<E>]) => [T, E] +>[ { data: "foo", onSuccess: (dataArg) => { dataArg; }, error: 404, onError: (errorArg) => { errorArg; }, }, { data: true, onSuccess: (dataArg) => { dataArg; }, error: 500, onError: (errorArg) => { errorArg; }, },] : ({ data: string; onSuccess: (dataArg: string) => void; error: number; onError: (errorArg: number) => void; } | { data: true; onSuccess: (dataArg: boolean) => void; error: number; onError: (errorArg: number) => void; })[] + { +>{ data: "foo", onSuccess: (dataArg) => { dataArg; }, error: 404, onError: (errorArg) => { errorArg; }, } : { data: string; onSuccess: (dataArg: string) => void; error: number; onError: (errorArg: number) => void; } + + data: "foo", +>data : string +>"foo" : "foo" + + onSuccess: (dataArg) => { +>onSuccess : (dataArg: string) => void +>(dataArg) => { dataArg; } : (dataArg: string) => void +>dataArg : string + + dataArg; +>dataArg : string + + }, + error: 404, +>error : number +>404 : 404 + + onError: (errorArg) => { +>onError : (errorArg: number) => void +>(errorArg) => { errorArg; } : (errorArg: number) => void +>errorArg : number + + errorArg; +>errorArg : number + + }, + }, + { +>{ data: true, onSuccess: (dataArg) => { dataArg; }, error: 500, onError: (errorArg) => { errorArg; }, } : { data: true; onSuccess: (dataArg: boolean) => void; error: number; onError: (errorArg: number) => void; } + + data: true, +>data : true +>true : true + + onSuccess: (dataArg) => { +>onSuccess : (dataArg: boolean) => void +>(dataArg) => { dataArg; } : (dataArg: boolean) => void +>dataArg : boolean + + dataArg; +>dataArg : boolean + + }, + error: 500, +>error : number +>500 : 500 + + onError: (errorArg) => { +>onError : (errorArg: number) => void +>(errorArg) => { errorArg; } : (errorArg: number) => void +>errorArg : number + + errorArg; +>errorArg : number + + }, + }, +]); + +type Tuple<T> = readonly [T, ...T[]]; +>Tuple : Tuple<T> + +declare function withTuplesConstraints<T extends Tuple<any>, E extends Tuple<any>>( +>withTuplesConstraints : <T extends Tuple<any>, E extends Tuple<any>>(arg: Results<T> & Errors<E>) => [T, E] + + arg: Results<T> & Errors<E> +>arg : Results<T> & Errors<E> + +): [T, E]; + +const res3 = withTuplesConstraints([ +>res3 : [[string, boolean], [number, number]] +>withTuplesConstraints([ { data: "foo", onSuccess: (dataArg) => { dataArg; }, error: 404, onError: (errorArg) => { errorArg; }, }, { data: true, onSuccess: (dataArg) => { dataArg; }, error: 500, onError: (errorArg) => { errorArg; }, },]) : [[string, boolean], [number, number]] +>withTuplesConstraints : <T extends Tuple<any>, E extends Tuple<any>>(arg: Results<T> & Errors<E>) => [T, E] +>[ { data: "foo", onSuccess: (dataArg) => { dataArg; }, error: 404, onError: (errorArg) => { errorArg; }, }, { data: true, onSuccess: (dataArg) => { dataArg; }, error: 500, onError: (errorArg) => { errorArg; }, },] : [{ data: string; onSuccess: (dataArg: string) => void; error: number; onError: (errorArg: number) => void; }, { data: true; onSuccess: (dataArg: boolean) => void; error: number; onError: (errorArg: number) => void; }] + { +>{ data: "foo", onSuccess: (dataArg) => { dataArg; }, error: 404, onError: (errorArg) => { errorArg; }, } : { data: string; onSuccess: (dataArg: string) => void; error: number; onError: (errorArg: number) => void; } + + data: "foo", +>data : string +>"foo" : "foo" + + onSuccess: (dataArg) => { +>onSuccess : (dataArg: string) => void +>(dataArg) => { dataArg; } : (dataArg: string) => void +>dataArg : string + + dataArg; +>dataArg : string + + }, + error: 404, +>error : number +>404 : 404 + + onError: (errorArg) => { +>onError : (errorArg: number) => void +>(errorArg) => { errorArg; } : (errorArg: number) => void +>errorArg : number + + errorArg; +>errorArg : number + + }, + }, + { +>{ data: true, onSuccess: (dataArg) => { dataArg; }, error: 500, onError: (errorArg) => { errorArg; }, } : { data: true; onSuccess: (dataArg: boolean) => void; error: number; onError: (errorArg: number) => void; } + + data: true, +>data : true +>true : true + + onSuccess: (dataArg) => { +>onSuccess : (dataArg: boolean) => void +>(dataArg) => { dataArg; } : (dataArg: boolean) => void +>dataArg : boolean + + dataArg; +>dataArg : boolean + + }, + error: 500, +>error : number +>500 : 500 + + onError: (errorArg) => { +>onError : (errorArg: number) => void +>(errorArg) => { errorArg; } : (errorArg: number) => void +>errorArg : number + + errorArg; +>errorArg : number + + }, + }, +]); diff --git a/tests/cases/compiler/reverseMappedIntersectionInference.ts b/tests/cases/compiler/reverseMappedIntersectionInference.ts new file mode 100644 index 0000000000000..6d02e9c5fb612 --- /dev/null +++ b/tests/cases/compiler/reverseMappedIntersectionInference.ts @@ -0,0 +1,99 @@ +// @strict: true +// @noEmit: true + +type Results<T> = { + [K in keyof T]: { + data: T[K]; + onSuccess: (data: T[K]) => void; + }; +}; + +type Errors<E> = { + [K in keyof E]: { + error: E[K]; + onError: (data: E[K]) => void; + }; +}; + +declare function withKeyedObj<T, E>( + arg: Results<T> & Errors<E> +): [T, E]; + +const res = withKeyedObj({ + a: { + data: "foo", + onSuccess: (dataArg) => { + dataArg; + }, + error: 404, + onError: (errorArg) => { + errorArg; + }, + }, + b: { + data: true, + onSuccess: (dataArg) => { + dataArg; + }, + error: 500, + onError: (errorArg) => { + errorArg; + }, + }, +}); + +declare function withTuples<T extends any[], E extends any[]>( + arg: [...(Results<T> & Errors<E>)] +): [T, E]; + +const res2 = withTuples([ + { + data: "foo", + onSuccess: (dataArg) => { + dataArg; + }, + error: 404, + onError: (errorArg) => { + errorArg; + }, + }, + { + data: true, + onSuccess: (dataArg) => { + dataArg; + }, + error: 500, + onError: (errorArg) => { + errorArg; + }, + }, +]); + +type Tuple<T> = readonly [T, ...T[]]; + +declare function withTuplesConstraints<T extends Tuple<any>, E extends Tuple<any>>( + arg: Results<T> & Errors<E> +): [T, E]; + +const res3 = withTuplesConstraints([ + { + data: "foo", + onSuccess: (dataArg) => { + dataArg; + }, + error: 404, + onError: (errorArg) => { + errorArg; + }, + }, + { + data: true, + onSuccess: (dataArg) => { + dataArg; + }, + error: 500, + onError: (errorArg) => { + errorArg; + }, + }, +]); \ No newline at end of file