diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 483b7f9c69..8c78e6c701 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10579,7 +10579,7 @@ namespace ts { priority = savePriority; } } - else if (source.flags & TypeFlags.UnionOrIntersection) { + else if (source.flags & TypeFlags.Union) { // Source is a union or intersection type, infer from each constituent type const sourceTypes = (source).types; for (const sourceType of sourceTypes) { @@ -10588,7 +10588,7 @@ namespace ts { } else { source = getApparentType(source); - if (source.flags & TypeFlags.Object) { + if (source.flags & (TypeFlags.Object | TypeFlags.Intersection)) { const key = source.id + "," + target.id; if (visited && visited.get(key)) { return; @@ -10668,7 +10668,7 @@ namespace ts { function inferFromProperties(source: Type, target: Type) { const properties = getPropertiesOfObjectType(target); for (const targetProp of properties) { - const sourceProp = getPropertyOfObjectType(source, targetProp.escapedName); + const sourceProp = getPropertyOfType(source, targetProp.escapedName); if (sourceProp) { inferFromTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp)); } diff --git a/tests/baselines/reference/intersectionTypeInference2.js b/tests/baselines/reference/intersectionTypeInference2.js new file mode 100644 index 0000000000..804b8d8520 --- /dev/null +++ b/tests/baselines/reference/intersectionTypeInference2.js @@ -0,0 +1,23 @@ +//// [intersectionTypeInference2.ts] +declare function f(x: { prop: T }): T; + +declare const a: { prop: string } & { prop: number }; +declare const b: { prop: string & number }; + +f(a); // string & number +f(b); // string & number + +// Repro from #18354 + +declare function f2(obj: {[K in keyof T]: T[K]}, key: Key): T[Key]; + +declare const obj: { a: string } & { b: string }; +f2(obj, 'a'); +f2(obj, 'b'); + + +//// [intersectionTypeInference2.js] +f(a); // string & number +f(b); // string & number +f2(obj, 'a'); +f2(obj, 'b'); diff --git a/tests/baselines/reference/intersectionTypeInference2.symbols b/tests/baselines/reference/intersectionTypeInference2.symbols new file mode 100644 index 0000000000..b04de204de --- /dev/null +++ b/tests/baselines/reference/intersectionTypeInference2.symbols @@ -0,0 +1,56 @@ +=== tests/cases/conformance/types/intersection/intersectionTypeInference2.ts === +declare function f(x: { prop: T }): T; +>f : Symbol(f, Decl(intersectionTypeInference2.ts, 0, 0)) +>T : Symbol(T, Decl(intersectionTypeInference2.ts, 0, 19)) +>x : Symbol(x, Decl(intersectionTypeInference2.ts, 0, 22)) +>prop : Symbol(prop, Decl(intersectionTypeInference2.ts, 0, 26)) +>T : Symbol(T, Decl(intersectionTypeInference2.ts, 0, 19)) +>T : Symbol(T, Decl(intersectionTypeInference2.ts, 0, 19)) + +declare const a: { prop: string } & { prop: number }; +>a : Symbol(a, Decl(intersectionTypeInference2.ts, 2, 13)) +>prop : Symbol(prop, Decl(intersectionTypeInference2.ts, 2, 18)) +>prop : Symbol(prop, Decl(intersectionTypeInference2.ts, 2, 37)) + +declare const b: { prop: string & number }; +>b : Symbol(b, Decl(intersectionTypeInference2.ts, 3, 13)) +>prop : Symbol(prop, Decl(intersectionTypeInference2.ts, 3, 18)) + +f(a); // string & number +>f : Symbol(f, Decl(intersectionTypeInference2.ts, 0, 0)) +>a : Symbol(a, Decl(intersectionTypeInference2.ts, 2, 13)) + +f(b); // string & number +>f : Symbol(f, Decl(intersectionTypeInference2.ts, 0, 0)) +>b : Symbol(b, Decl(intersectionTypeInference2.ts, 3, 13)) + +// Repro from #18354 + +declare function f2(obj: {[K in keyof T]: T[K]}, key: Key): T[Key]; +>f2 : Symbol(f2, Decl(intersectionTypeInference2.ts, 6, 5)) +>T : Symbol(T, Decl(intersectionTypeInference2.ts, 10, 20)) +>Key : Symbol(Key, Decl(intersectionTypeInference2.ts, 10, 22)) +>T : Symbol(T, Decl(intersectionTypeInference2.ts, 10, 20)) +>obj : Symbol(obj, Decl(intersectionTypeInference2.ts, 10, 44)) +>K : Symbol(K, Decl(intersectionTypeInference2.ts, 10, 51)) +>T : Symbol(T, Decl(intersectionTypeInference2.ts, 10, 20)) +>T : Symbol(T, Decl(intersectionTypeInference2.ts, 10, 20)) +>K : Symbol(K, Decl(intersectionTypeInference2.ts, 10, 51)) +>key : Symbol(key, Decl(intersectionTypeInference2.ts, 10, 72)) +>Key : Symbol(Key, Decl(intersectionTypeInference2.ts, 10, 22)) +>T : Symbol(T, Decl(intersectionTypeInference2.ts, 10, 20)) +>Key : Symbol(Key, Decl(intersectionTypeInference2.ts, 10, 22)) + +declare const obj: { a: string } & { b: string }; +>obj : Symbol(obj, Decl(intersectionTypeInference2.ts, 12, 13)) +>a : Symbol(a, Decl(intersectionTypeInference2.ts, 12, 20)) +>b : Symbol(b, Decl(intersectionTypeInference2.ts, 12, 36)) + +f2(obj, 'a'); +>f2 : Symbol(f2, Decl(intersectionTypeInference2.ts, 6, 5)) +>obj : Symbol(obj, Decl(intersectionTypeInference2.ts, 12, 13)) + +f2(obj, 'b'); +>f2 : Symbol(f2, Decl(intersectionTypeInference2.ts, 6, 5)) +>obj : Symbol(obj, Decl(intersectionTypeInference2.ts, 12, 13)) + diff --git a/tests/baselines/reference/intersectionTypeInference2.types b/tests/baselines/reference/intersectionTypeInference2.types new file mode 100644 index 0000000000..41e1b3483c --- /dev/null +++ b/tests/baselines/reference/intersectionTypeInference2.types @@ -0,0 +1,62 @@ +=== tests/cases/conformance/types/intersection/intersectionTypeInference2.ts === +declare function f(x: { prop: T }): T; +>f : (x: { prop: T; }) => T +>T : T +>x : { prop: T; } +>prop : T +>T : T +>T : T + +declare const a: { prop: string } & { prop: number }; +>a : { prop: string; } & { prop: number; } +>prop : string +>prop : number + +declare const b: { prop: string & number }; +>b : { prop: string & number; } +>prop : string & number + +f(a); // string & number +>f(a) : string & number +>f : (x: { prop: T; }) => T +>a : { prop: string; } & { prop: number; } + +f(b); // string & number +>f(b) : string & number +>f : (x: { prop: T; }) => T +>b : { prop: string & number; } + +// Repro from #18354 + +declare function f2(obj: {[K in keyof T]: T[K]}, key: Key): T[Key]; +>f2 : (obj: { [K in keyof T]: T[K]; }, key: Key) => T[Key] +>T : T +>Key : Key +>T : T +>obj : { [K in keyof T]: T[K]; } +>K : K +>T : T +>T : T +>K : K +>key : Key +>Key : Key +>T : T +>Key : Key + +declare const obj: { a: string } & { b: string }; +>obj : { a: string; } & { b: string; } +>a : string +>b : string + +f2(obj, 'a'); +>f2(obj, 'a') : string +>f2 : (obj: { [K in keyof T]: T[K]; }, key: Key) => T[Key] +>obj : { a: string; } & { b: string; } +>'a' : "a" + +f2(obj, 'b'); +>f2(obj, 'b') : string +>f2 : (obj: { [K in keyof T]: T[K]; }, key: Key) => T[Key] +>obj : { a: string; } & { b: string; } +>'b' : "b" + diff --git a/tests/cases/conformance/types/intersection/intersectionTypeInference2.ts b/tests/cases/conformance/types/intersection/intersectionTypeInference2.ts new file mode 100644 index 0000000000..d32441cfee --- /dev/null +++ b/tests/cases/conformance/types/intersection/intersectionTypeInference2.ts @@ -0,0 +1,15 @@ +declare function f(x: { prop: T }): T; + +declare const a: { prop: string } & { prop: number }; +declare const b: { prop: string & number }; + +f(a); // string & number +f(b); // string & number + +// Repro from #18354 + +declare function f2(obj: {[K in keyof T]: T[K]}, key: Key): T[Key]; + +declare const obj: { a: string } & { b: string }; +f2(obj, 'a'); +f2(obj, 'b'); diff --git a/tests/cases/fourslash/tsxQuickInfo6.ts b/tests/cases/fourslash/tsxQuickInfo6.ts index e125336e51..515928b773 100644 --- a/tests/cases/fourslash/tsxQuickInfo6.ts +++ b/tests/cases/fourslash/tsxQuickInfo6.ts @@ -15,5 +15,5 @@ verify.quickInfos({ 1: "function ComponentSpecific(l: {\n prop: number;\n}): any", - 2: "function ComponentSpecific(l: {\n prop: number;\n}): any" + 2: "function ComponentSpecific(l: {\n prop: number & \"hello\";\n}): any" }); diff --git a/tests/cases/fourslash/tsxQuickInfo7.ts b/tests/cases/fourslash/tsxQuickInfo7.ts index d0ec1916b4..72de112804 100644 --- a/tests/cases/fourslash/tsxQuickInfo7.ts +++ b/tests/cases/fourslash/tsxQuickInfo7.ts @@ -24,6 +24,6 @@ verify.quickInfos({ 3: "function OverloadComponent(attr: {\n b: string;\n a: boolean;\n}): any (+2 overloads)", 4: "function OverloadComponent(attr: {\n b: number;\n a?: string;\n \"ignore-prop\": boolean;\n}): any (+2 overloads)", 5: "function OverloadComponent(): any (+2 overloads)", - 6: "function OverloadComponent(attr: {\n b: string;\n a: boolean;\n}): any (+2 overloads)", - 7: "function OverloadComponent(attr: {\n b: number;\n a: boolean;\n}): any (+2 overloads)", + 6: "function OverloadComponent(attr: {\n b: string & number;\n a: boolean;\n}): any (+2 overloads)", + 7: "function OverloadComponent(attr: {\n b: number & string;\n a: boolean;\n}): any (+2 overloads)", });