Skip to content

Commit 0268003

Browse files
committed
Forbid contravariant inferences to conditional type branches
1 parent a9ad94a commit 0268003

5 files changed

+218
-1
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14662,7 +14662,7 @@ namespace ts {
1466214662
inferFromTypes(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target));
1466314663
inferFromTypes(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target));
1466414664
}
14665-
else if (target.flags & TypeFlags.Conditional) {
14665+
else if (target.flags & TypeFlags.Conditional && !contravariant) {
1466614666
inferFromTypes(source, getTrueTypeFromConditionalType(<ConditionalType>target));
1466714667
inferFromTypes(source, getFalseTypeFromConditionalType(<ConditionalType>target));
1466814668
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//// [overloadedConstructorFixesInferencesAppropriately.ts]
2+
interface Box<T> {
3+
v: T;
4+
}
5+
6+
interface ErrorResult {
7+
readonly error: true
8+
}
9+
10+
interface AsyncLoaderProps<TResult extends {}> {
11+
readonly asyncLoad: () => Box<TResult>;
12+
readonly children: (result: Exclude<TResult, ErrorResult>) => string;
13+
}
14+
15+
class AsyncLoader<TResult extends {}> {
16+
constructor(props: string, context: any);
17+
constructor(props: AsyncLoaderProps<TResult>);
18+
constructor(...args: any[]) {}
19+
}
20+
21+
function load(): Box<{ success: true } | ErrorResult> {
22+
return null as any;
23+
}
24+
25+
new AsyncLoader({
26+
asyncLoad: load,
27+
children: result => result.success as any,
28+
}); // should work fine
29+
30+
31+
//// [overloadedConstructorFixesInferencesAppropriately.js]
32+
"use strict";
33+
var AsyncLoader = /** @class */ (function () {
34+
function AsyncLoader() {
35+
var args = [];
36+
for (var _i = 0; _i < arguments.length; _i++) {
37+
args[_i] = arguments[_i];
38+
}
39+
}
40+
return AsyncLoader;
41+
}());
42+
function load() {
43+
return null;
44+
}
45+
new AsyncLoader({
46+
asyncLoad: load,
47+
children: function (result) { return result.success; }
48+
}); // should work fine
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
=== tests/cases/compiler/overloadedConstructorFixesInferencesAppropriately.ts ===
2+
interface Box<T> {
3+
>Box : Symbol(Box, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 0, 0))
4+
>T : Symbol(T, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 0, 14))
5+
6+
v: T;
7+
>v : Symbol(Box.v, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 0, 18))
8+
>T : Symbol(T, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 0, 14))
9+
}
10+
11+
interface ErrorResult {
12+
>ErrorResult : Symbol(ErrorResult, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 2, 1))
13+
14+
readonly error: true
15+
>error : Symbol(ErrorResult.error, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 4, 23))
16+
}
17+
18+
interface AsyncLoaderProps<TResult extends {}> {
19+
>AsyncLoaderProps : Symbol(AsyncLoaderProps, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 6, 1))
20+
>TResult : Symbol(TResult, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 8, 27))
21+
22+
readonly asyncLoad: () => Box<TResult>;
23+
>asyncLoad : Symbol(AsyncLoaderProps.asyncLoad, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 8, 48))
24+
>Box : Symbol(Box, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 0, 0))
25+
>TResult : Symbol(TResult, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 8, 27))
26+
27+
readonly children: (result: Exclude<TResult, ErrorResult>) => string;
28+
>children : Symbol(AsyncLoaderProps.children, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 9, 43))
29+
>result : Symbol(result, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 10, 24))
30+
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
31+
>TResult : Symbol(TResult, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 8, 27))
32+
>ErrorResult : Symbol(ErrorResult, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 2, 1))
33+
}
34+
35+
class AsyncLoader<TResult extends {}> {
36+
>AsyncLoader : Symbol(AsyncLoader, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 11, 1))
37+
>TResult : Symbol(TResult, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 13, 18))
38+
39+
constructor(props: string, context: any);
40+
>props : Symbol(props, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 14, 16))
41+
>context : Symbol(context, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 14, 30))
42+
43+
constructor(props: AsyncLoaderProps<TResult>);
44+
>props : Symbol(props, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 15, 16))
45+
>AsyncLoaderProps : Symbol(AsyncLoaderProps, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 6, 1))
46+
>TResult : Symbol(TResult, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 13, 18))
47+
48+
constructor(...args: any[]) {}
49+
>args : Symbol(args, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 16, 16))
50+
}
51+
52+
function load(): Box<{ success: true } | ErrorResult> {
53+
>load : Symbol(load, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 17, 1))
54+
>Box : Symbol(Box, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 0, 0))
55+
>success : Symbol(success, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 19, 22))
56+
>ErrorResult : Symbol(ErrorResult, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 2, 1))
57+
58+
return null as any;
59+
}
60+
61+
new AsyncLoader({
62+
>AsyncLoader : Symbol(AsyncLoader, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 11, 1))
63+
64+
asyncLoad: load,
65+
>asyncLoad : Symbol(asyncLoad, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 23, 17))
66+
>load : Symbol(load, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 17, 1))
67+
68+
children: result => result.success as any,
69+
>children : Symbol(children, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 24, 20))
70+
>result : Symbol(result, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 25, 13))
71+
>result.success : Symbol(success, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 19, 22))
72+
>result : Symbol(result, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 25, 13))
73+
>success : Symbol(success, Decl(overloadedConstructorFixesInferencesAppropriately.ts, 19, 22))
74+
75+
}); // should work fine
76+
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
=== tests/cases/compiler/overloadedConstructorFixesInferencesAppropriately.ts ===
2+
interface Box<T> {
3+
v: T;
4+
>v : T
5+
}
6+
7+
interface ErrorResult {
8+
readonly error: true
9+
>error : true
10+
>true : true
11+
}
12+
13+
interface AsyncLoaderProps<TResult extends {}> {
14+
readonly asyncLoad: () => Box<TResult>;
15+
>asyncLoad : () => Box<TResult>
16+
17+
readonly children: (result: Exclude<TResult, ErrorResult>) => string;
18+
>children : (result: Exclude<TResult, ErrorResult>) => string
19+
>result : Exclude<TResult, ErrorResult>
20+
}
21+
22+
class AsyncLoader<TResult extends {}> {
23+
>AsyncLoader : AsyncLoader<TResult>
24+
25+
constructor(props: string, context: any);
26+
>props : string
27+
>context : any
28+
29+
constructor(props: AsyncLoaderProps<TResult>);
30+
>props : AsyncLoaderProps<TResult>
31+
32+
constructor(...args: any[]) {}
33+
>args : any[]
34+
}
35+
36+
function load(): Box<{ success: true } | ErrorResult> {
37+
>load : () => Box<ErrorResult | { success: true; }>
38+
>success : true
39+
>true : true
40+
41+
return null as any;
42+
>null as any : any
43+
>null : null
44+
}
45+
46+
new AsyncLoader({
47+
>new AsyncLoader({ asyncLoad: load, children: result => result.success as any,}) : AsyncLoader<ErrorResult | { success: true; }>
48+
>AsyncLoader : typeof AsyncLoader
49+
>{ asyncLoad: load, children: result => result.success as any,} : { asyncLoad: () => Box<ErrorResult | { success: true; }>; children: (result: { success: true; }) => any; }
50+
51+
asyncLoad: load,
52+
>asyncLoad : () => Box<ErrorResult | { success: true; }>
53+
>load : () => Box<ErrorResult | { success: true; }>
54+
55+
children: result => result.success as any,
56+
>children : (result: { success: true; }) => any
57+
>result => result.success as any : (result: { success: true; }) => any
58+
>result : { success: true; }
59+
>result.success as any : any
60+
>result.success : true
61+
>result : { success: true; }
62+
>success : true
63+
64+
}); // should work fine
65+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// @strict: true
2+
interface Box<T> {
3+
v: T;
4+
}
5+
6+
interface ErrorResult {
7+
readonly error: true
8+
}
9+
10+
interface AsyncLoaderProps<TResult extends {}> {
11+
readonly asyncLoad: () => Box<TResult>;
12+
readonly children: (result: Exclude<TResult, ErrorResult>) => string;
13+
}
14+
15+
class AsyncLoader<TResult extends {}> {
16+
constructor(props: string, context: any);
17+
constructor(props: AsyncLoaderProps<TResult>);
18+
constructor(...args: any[]) {}
19+
}
20+
21+
function load(): Box<{ success: true } | ErrorResult> {
22+
return null as any;
23+
}
24+
25+
new AsyncLoader({
26+
asyncLoad: load,
27+
children: result => result.success as any,
28+
}); // should work fine

0 commit comments

Comments
 (0)