Skip to content

Commit f93f4f3

Browse files
authored
Merge pull request #29847 from Microsoft/inferToUnionTypes
Improve inference to union and intersection types
2 parents d66000b + ce6c04e commit f93f4f3

12 files changed

+180
-38
lines changed

src/compiler/checker.ts

+7-18
Original file line numberDiff line numberDiff line change
@@ -14473,26 +14473,15 @@ namespace ts {
1447314473
inferFromTypes(source, getUnionType([getTrueTypeFromConditionalType(<ConditionalType>target), getFalseTypeFromConditionalType(<ConditionalType>target)]));
1447414474
}
1447514475
else if (target.flags & TypeFlags.UnionOrIntersection) {
14476-
const targetTypes = (<UnionOrIntersectionType>target).types;
14477-
let typeVariableCount = 0;
14478-
let typeVariable: TypeParameter | IndexedAccessType | undefined;
14479-
// First infer to each type in union or intersection that isn't a type variable
14480-
for (const t of targetTypes) {
14476+
for (const t of (<UnionOrIntersectionType>target).types) {
14477+
const savePriority = priority;
14478+
// Inferences directly to naked type variables are given lower priority as they are
14479+
// less specific. For example, when inferring from Promise<string> to T | Promise<T>,
14480+
// we want to infer string for T, not Promise<string> | string.
1448114481
if (getInferenceInfoForType(t)) {
14482-
typeVariable = <InstantiableType>t;
14483-
typeVariableCount++;
14484-
}
14485-
else {
14486-
inferFromTypes(source, t);
14482+
priority |= InferencePriority.NakedTypeVariable;
1448714483
}
14488-
}
14489-
// Next, if target containings a single naked type variable, make a secondary inference to that type
14490-
// variable. This gives meaningful results for union types in co-variant positions and intersection
14491-
// types in contra-variant positions (such as callback parameters).
14492-
if (typeVariableCount === 1) {
14493-
const savePriority = priority;
14494-
priority |= InferencePriority.NakedTypeVariable;
14495-
inferFromTypes(source, typeVariable!);
14484+
inferFromTypes(source, t);
1449614485
priority = savePriority;
1449714486
}
1449814487
}

tests/baselines/reference/conditionalTypeDoesntSpinForever.types

+8-8
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,9 @@ export enum PubSubRecordIsStoredInRedisAsA {
120120

121121
buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString})) as
122122
>buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString})) as BuildPubSubRecordType<SO_FAR & {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString}> : BuildPubSubRecordType<SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString; }>
123-
>buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString})) : BuildPubSubRecordType<SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA; }>
123+
>buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString})) : BuildPubSubRecordType<SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString; }>
124124
>buildPubSubRecordType : <SO_FAR>(soFar: SO_FAR) => BuildPubSubRecordType<SO_FAR>
125-
>Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString}) : SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA; }
125+
>Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString}) : SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString; }
126126
>Object.assign : { <T, U>(target: T, source: U): T & U; <T, U, V>(target: T, source1: U, source2: V): T & U & V; <T, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W; (target: object, ...sources: any[]): any; }
127127
>Object : ObjectConstructor
128128
>assign : { <T, U>(target: T, source: U): T & U; <T, U, V>(target: T, source1: U, source2: V): T & U & V; <T, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W; (target: object, ...sources: any[]): any; }
@@ -144,9 +144,9 @@ export enum PubSubRecordIsStoredInRedisAsA {
144144

145145
buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash})) as
146146
>buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash})) as BuildPubSubRecordType<SO_FAR & {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash}> : BuildPubSubRecordType<SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA.redisHash; }>
147-
>buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash})) : BuildPubSubRecordType<SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA; }>
147+
>buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash})) : BuildPubSubRecordType<SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA.redisHash; }>
148148
>buildPubSubRecordType : <SO_FAR>(soFar: SO_FAR) => BuildPubSubRecordType<SO_FAR>
149-
>Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash}) : SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA; }
149+
>Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash}) : SO_FAR & { storedAs: PubSubRecordIsStoredInRedisAsA.redisHash; }
150150
>Object.assign : { <T, U>(target: T, source: U): T & U; <T, U, V>(target: T, source1: U, source2: V): T & U & V; <T, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W; (target: object, ...sources: any[]): any; }
151151
>Object : ObjectConstructor
152152
>assign : { <T, U>(target: T, source: U): T & U; <T, U, V>(target: T, source1: U, source2: V): T & U & V; <T, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W; (target: object, ...sources: any[]): any; }
@@ -337,16 +337,16 @@ export enum PubSubRecordIsStoredInRedisAsA {
337337

338338
buildPubSubRecordType(Object.assign({}, soFar, {maxMsToWaitBeforePublishing: 0})) as BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: 0}>,
339339
>buildPubSubRecordType(Object.assign({}, soFar, {maxMsToWaitBeforePublishing: 0})) as BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: 0}> : BuildPubSubRecordType<SO_FAR & { maxMsToWaitBeforePublishing: 0; }>
340-
>buildPubSubRecordType(Object.assign({}, soFar, {maxMsToWaitBeforePublishing: 0})) : BuildPubSubRecordType<SO_FAR & { maxMsToWaitBeforePublishing: number; }>
340+
>buildPubSubRecordType(Object.assign({}, soFar, {maxMsToWaitBeforePublishing: 0})) : BuildPubSubRecordType<SO_FAR & { maxMsToWaitBeforePublishing: 0; }>
341341
>buildPubSubRecordType : <SO_FAR>(soFar: SO_FAR) => BuildPubSubRecordType<SO_FAR>
342-
>Object.assign({}, soFar, {maxMsToWaitBeforePublishing: 0}) : SO_FAR & { maxMsToWaitBeforePublishing: number; }
342+
>Object.assign({}, soFar, {maxMsToWaitBeforePublishing: 0}) : SO_FAR & { maxMsToWaitBeforePublishing: 0; }
343343
>Object.assign : { <T, U>(target: T, source: U): T & U; <T, U, V>(target: T, source1: U, source2: V): T & U & V; <T, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W; (target: object, ...sources: any[]): any; }
344344
>Object : ObjectConstructor
345345
>assign : { <T, U>(target: T, source: U): T & U; <T, U, V>(target: T, source1: U, source2: V): T & U & V; <T, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W; (target: object, ...sources: any[]): any; }
346346
>{} : {}
347347
>soFar : SO_FAR
348-
>{maxMsToWaitBeforePublishing: 0} : { maxMsToWaitBeforePublishing: number; }
349-
>maxMsToWaitBeforePublishing : number
348+
>{maxMsToWaitBeforePublishing: 0} : { maxMsToWaitBeforePublishing: 0; }
349+
>maxMsToWaitBeforePublishing : 0
350350
>0 : 0
351351
>maxMsToWaitBeforePublishing : 0
352352
}

tests/baselines/reference/jqueryInference.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ declare function shouldBeIdentity<T, U>(p: DoNothingAlias<T, U>): MyPromise<T, U
1111

1212
declare const p1: MyPromise<boolean, any>;
1313
var p2 = shouldBeIdentity(p1);
14-
var p2: MyPromise<boolean, {}>;
14+
var p2: MyPromise<boolean, any>;
1515

1616

1717
//// [jqueryInference.js]

tests/baselines/reference/jqueryInference.symbols

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ var p2 = shouldBeIdentity(p1);
4848
>shouldBeIdentity : Symbol(shouldBeIdentity, Decl(jqueryInference.ts, 6, 58))
4949
>p1 : Symbol(p1, Decl(jqueryInference.ts, 10, 13))
5050

51-
var p2: MyPromise<boolean, {}>;
51+
var p2: MyPromise<boolean, any>;
5252
>p2 : Symbol(p2, Decl(jqueryInference.ts, 11, 3), Decl(jqueryInference.ts, 12, 3))
5353
>MyPromise : Symbol(MyPromise, Decl(jqueryInference.ts, 0, 0))
5454

tests/baselines/reference/jqueryInference.types

+4-4
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ declare const p1: MyPromise<boolean, any>;
2222
>p1 : MyPromise<boolean, any>
2323

2424
var p2 = shouldBeIdentity(p1);
25-
>p2 : MyPromise<boolean, {}>
26-
>shouldBeIdentity(p1) : MyPromise<boolean, {}>
25+
>p2 : MyPromise<boolean, any>
26+
>shouldBeIdentity(p1) : MyPromise<boolean, any>
2727
>shouldBeIdentity : <T, U>(p: DoNothingAlias<T, U>) => MyPromise<T, U>
2828
>p1 : MyPromise<boolean, any>
2929

30-
var p2: MyPromise<boolean, {}>;
31-
>p2 : MyPromise<boolean, {}>
30+
var p2: MyPromise<boolean, any>;
31+
>p2 : MyPromise<boolean, any>
3232

tests/baselines/reference/objectSpread.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ let exclusive: { id: string, a: number, b: string, c: string, d: boolean } =
602602
>d : boolean
603603

604604
f({ a: 1, b: 'yes' }, { c: 'no', d: false })
605-
>f({ a: 1, b: 'yes' }, { c: 'no', d: false }) : { a: number; b: string; } & { c: string; d: boolean; } & { id: string; }
605+
>f({ a: 1, b: 'yes' }, { c: 'no', d: false }) : { a: number; b: string; } & { c: string; d: false; } & { id: string; }
606606
>f : <T, U>(t: T, u: U) => T & U & { id: string; }
607607
>{ a: 1, b: 'yes' } : { a: number; b: string; }
608608
>a : number

tests/baselines/reference/restTupleElements1.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ f0([]); // Error
173173
>[] : never[]
174174

175175
f0([1]);
176-
>f0([1]) : [number, {}]
176+
>f0([1]) : [number, number]
177177
>f0 : <T, U>(x: [T, ...U[]]) => [T, U]
178178
>[1] : [number]
179179
>1 : 1

tests/baselines/reference/unionAndIntersectionInference1.js

+26-2
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,24 @@ declare var mbp: Man & Bear;
7171

7272
pigify(mbp).oinks; // OK, mbp is treated as Pig
7373
pigify(mbp).walks; // Ok, mbp is treated as Man
74+
75+
// Repros from #29815
76+
77+
interface ITest {
78+
name: 'test'
79+
}
80+
81+
const createTestAsync = (): Promise<ITest> => Promise.resolve().then(() => ({ name: 'test' }))
82+
83+
const createTest = (): ITest => {
84+
return { name: 'test' }
85+
}
86+
87+
declare function f1<T, U>(x: T | U): T | U;
88+
declare function f2<T, U>(x: T & U): T & U;
89+
90+
let x1: string = f1('a');
91+
let x2: string = f2('a');
7492

7593

7694
//// [unionAndIntersectionInference1.js]
@@ -80,7 +98,7 @@ function destructure(something, haveValue, haveY) {
8098
return something === y ? haveY(y) : haveValue(something);
8199
}
82100
var value = Math.random() > 0.5 ? 'hey!' : undefined;
83-
var result = destructure(value, function (text) { return 'string'; }, function (y) { return 'other one'; }); // text: string, y: Y
101+
var result = destructure(value, text => 'string', y => 'other one'); // text: string, y: Y
84102
// Repro from #4212
85103
function isVoid(value) {
86104
return undefined;
@@ -107,7 +125,13 @@ function baz1(value) {
107125
function get(x) {
108126
return null; // just an example
109127
}
110-
var foo;
128+
let foo;
111129
get(foo).toUpperCase(); // Ok
112130
pigify(mbp).oinks; // OK, mbp is treated as Pig
113131
pigify(mbp).walks; // Ok, mbp is treated as Man
132+
const createTestAsync = () => Promise.resolve().then(() => ({ name: 'test' }));
133+
const createTest = () => {
134+
return { name: 'test' };
135+
};
136+
let x1 = f1('a');
137+
let x2 = f2('a');

tests/baselines/reference/unionAndIntersectionInference1.symbols

+57-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ function destructure<a, r>(
5050
var value = Math.random() > 0.5 ? 'hey!' : <Y>undefined;
5151
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 12, 3))
5252
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
53-
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
53+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
5454
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
5555
>Y : Symbol(Y, Decl(unionAndIntersectionInference1.ts, 0, 0))
5656
>undefined : Symbol(undefined)
@@ -201,3 +201,59 @@ pigify(mbp).walks; // Ok, mbp is treated as Man
201201
>mbp : Symbol(mbp, Decl(unionAndIntersectionInference1.ts, 68, 11))
202202
>walks : Symbol(Man.walks, Decl(unionAndIntersectionInference1.ts, 55, 15))
203203

204+
// Repros from #29815
205+
206+
interface ITest {
207+
>ITest : Symbol(ITest, Decl(unionAndIntersectionInference1.ts, 71, 18))
208+
209+
name: 'test'
210+
>name : Symbol(ITest.name, Decl(unionAndIntersectionInference1.ts, 75, 17))
211+
}
212+
213+
const createTestAsync = (): Promise<ITest> => Promise.resolve().then(() => ({ name: 'test' }))
214+
>createTestAsync : Symbol(createTestAsync, Decl(unionAndIntersectionInference1.ts, 79, 5))
215+
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
216+
>ITest : Symbol(ITest, Decl(unionAndIntersectionInference1.ts, 71, 18))
217+
>Promise.resolve().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
218+
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
219+
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
220+
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
221+
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
222+
>name : Symbol(name, Decl(unionAndIntersectionInference1.ts, 79, 77))
223+
224+
const createTest = (): ITest => {
225+
>createTest : Symbol(createTest, Decl(unionAndIntersectionInference1.ts, 81, 5))
226+
>ITest : Symbol(ITest, Decl(unionAndIntersectionInference1.ts, 71, 18))
227+
228+
return { name: 'test' }
229+
>name : Symbol(name, Decl(unionAndIntersectionInference1.ts, 82, 10))
230+
}
231+
232+
declare function f1<T, U>(x: T | U): T | U;
233+
>f1 : Symbol(f1, Decl(unionAndIntersectionInference1.ts, 83, 1))
234+
>T : Symbol(T, Decl(unionAndIntersectionInference1.ts, 85, 20))
235+
>U : Symbol(U, Decl(unionAndIntersectionInference1.ts, 85, 22))
236+
>x : Symbol(x, Decl(unionAndIntersectionInference1.ts, 85, 26))
237+
>T : Symbol(T, Decl(unionAndIntersectionInference1.ts, 85, 20))
238+
>U : Symbol(U, Decl(unionAndIntersectionInference1.ts, 85, 22))
239+
>T : Symbol(T, Decl(unionAndIntersectionInference1.ts, 85, 20))
240+
>U : Symbol(U, Decl(unionAndIntersectionInference1.ts, 85, 22))
241+
242+
declare function f2<T, U>(x: T & U): T & U;
243+
>f2 : Symbol(f2, Decl(unionAndIntersectionInference1.ts, 85, 43))
244+
>T : Symbol(T, Decl(unionAndIntersectionInference1.ts, 86, 20))
245+
>U : Symbol(U, Decl(unionAndIntersectionInference1.ts, 86, 22))
246+
>x : Symbol(x, Decl(unionAndIntersectionInference1.ts, 86, 26))
247+
>T : Symbol(T, Decl(unionAndIntersectionInference1.ts, 86, 20))
248+
>U : Symbol(U, Decl(unionAndIntersectionInference1.ts, 86, 22))
249+
>T : Symbol(T, Decl(unionAndIntersectionInference1.ts, 86, 20))
250+
>U : Symbol(U, Decl(unionAndIntersectionInference1.ts, 86, 22))
251+
252+
let x1: string = f1('a');
253+
>x1 : Symbol(x1, Decl(unionAndIntersectionInference1.ts, 88, 3))
254+
>f1 : Symbol(f1, Decl(unionAndIntersectionInference1.ts, 83, 1))
255+
256+
let x2: string = f2('a');
257+
>x2 : Symbol(x2, Decl(unionAndIntersectionInference1.ts, 89, 3))
258+
>f2 : Symbol(f2, Decl(unionAndIntersectionInference1.ts, 85, 43))
259+

tests/baselines/reference/unionAndIntersectionInference1.types

+53
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,56 @@ pigify(mbp).walks; // Ok, mbp is treated as Man
179179
>mbp : Man & Bear
180180
>walks : boolean
181181

182+
// Repros from #29815
183+
184+
interface ITest {
185+
name: 'test'
186+
>name : "test"
187+
}
188+
189+
const createTestAsync = (): Promise<ITest> => Promise.resolve().then(() => ({ name: 'test' }))
190+
>createTestAsync : () => Promise<ITest>
191+
>(): Promise<ITest> => Promise.resolve().then(() => ({ name: 'test' })) : () => Promise<ITest>
192+
>Promise.resolve().then(() => ({ name: 'test' })) : Promise<ITest | { name: "test"; }>
193+
>Promise.resolve().then : <TResult1 = void, TResult2 = never>(onfulfilled?: (value: void) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
194+
>Promise.resolve() : Promise<void>
195+
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
196+
>Promise : PromiseConstructor
197+
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
198+
>then : <TResult1 = void, TResult2 = never>(onfulfilled?: (value: void) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
199+
>() => ({ name: 'test' }) : () => { name: "test"; }
200+
>({ name: 'test' }) : { name: "test"; }
201+
>{ name: 'test' } : { name: "test"; }
202+
>name : "test"
203+
>'test' : "test"
204+
205+
const createTest = (): ITest => {
206+
>createTest : () => ITest
207+
>(): ITest => { return { name: 'test' }} : () => ITest
208+
209+
return { name: 'test' }
210+
>{ name: 'test' } : { name: "test"; }
211+
>name : "test"
212+
>'test' : "test"
213+
}
214+
215+
declare function f1<T, U>(x: T | U): T | U;
216+
>f1 : <T, U>(x: T | U) => T | U
217+
>x : T | U
218+
219+
declare function f2<T, U>(x: T & U): T & U;
220+
>f2 : <T, U>(x: T & U) => T & U
221+
>x : T & U
222+
223+
let x1: string = f1('a');
224+
>x1 : string
225+
>f1('a') : "a"
226+
>f1 : <T, U>(x: T | U) => T | U
227+
>'a' : "a"
228+
229+
let x2: string = f2('a');
230+
>x2 : string
231+
>f2('a') : "a"
232+
>f2 : <T, U>(x: T & U) => T & U
233+
>'a' : "a"
234+

tests/cases/compiler/jqueryInference.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ declare function shouldBeIdentity<T, U>(p: DoNothingAlias<T, U>): MyPromise<T, U
1010

1111
declare const p1: MyPromise<boolean, any>;
1212
var p2 = shouldBeIdentity(p1);
13-
var p2: MyPromise<boolean, {}>;
13+
var p2: MyPromise<boolean, any>;

tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference1.ts

+20
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// @target: es2015
2+
13
// Repro from #2264
24

35
interface Y { 'i am a very certain type': Y }
@@ -70,3 +72,21 @@ declare var mbp: Man & Bear;
7072

7173
pigify(mbp).oinks; // OK, mbp is treated as Pig
7274
pigify(mbp).walks; // Ok, mbp is treated as Man
75+
76+
// Repros from #29815
77+
78+
interface ITest {
79+
name: 'test'
80+
}
81+
82+
const createTestAsync = (): Promise<ITest> => Promise.resolve().then(() => ({ name: 'test' }))
83+
84+
const createTest = (): ITest => {
85+
return { name: 'test' }
86+
}
87+
88+
declare function f1<T, U>(x: T | U): T | U;
89+
declare function f2<T, U>(x: T & U): T & U;
90+
91+
let x1: string = f1('a');
92+
let x2: string = f2('a');

0 commit comments

Comments
 (0)