Skip to content

Commit 3f483d8

Browse files
authored
No contextual typing from return types for boolean literals (microsoft#48380)
* No contextual typing from return types for boolean literals * Accept new baselines * Add regression tests
1 parent e62f960 commit 3f483d8

7 files changed

+230
-11
lines changed

Diff for: src/compiler/checker.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -26902,9 +26902,14 @@ namespace ts {
2690226902
return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper);
2690326903
}
2690426904
// For other purposes (e.g. determining whether to produce literal types) we only
26905-
// incorporate inferences made from the return type in a function call.
26905+
// incorporate inferences made from the return type in a function call. We remove
26906+
// the 'boolean' type from the contextual type such that contextually typed boolean
26907+
// literals actually end up widening to 'boolean' (see #48363).
2690626908
if (inferenceContext.returnMapper) {
26907-
return instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper);
26909+
const type = instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper);
26910+
return type.flags & TypeFlags.Union && containsType((type as UnionType).types, regularFalseType) && containsType((type as UnionType).types, regularTrueType) ?
26911+
filterType(type, t => t !== regularFalseType && t !== regularTrueType) :
26912+
type;
2690826913
}
2690926914
}
2691026915
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//// [contextuallyTypedBooleanLiterals.ts]
2+
// Repro from #48363
3+
4+
type Box<T> = {
5+
get: () => T,
6+
set: (value: T) => void
7+
}
8+
9+
declare function box<T>(value: T): Box<T>;
10+
11+
const bn1 = box(0); // Box<number>
12+
const bn2: Box<number> = box(0); // Ok
13+
14+
const bb1 = box(false); // Box<boolean>
15+
const bb2: Box<boolean> = box(false); // Error, box<false> not assignable to Box<boolean>
16+
17+
// Repro from #48150
18+
19+
interface Observable<T>
20+
{
21+
(): T;
22+
(value: T): any;
23+
}
24+
25+
declare function observable<T>(value: T): Observable<T>;
26+
27+
const x: Observable<boolean> = observable(false);
28+
29+
30+
//// [contextuallyTypedBooleanLiterals.js]
31+
"use strict";
32+
// Repro from #48363
33+
var bn1 = box(0); // Box<number>
34+
var bn2 = box(0); // Ok
35+
var bb1 = box(false); // Box<boolean>
36+
var bb2 = box(false); // Error, box<false> not assignable to Box<boolean>
37+
var x = observable(false);
38+
39+
40+
//// [contextuallyTypedBooleanLiterals.d.ts]
41+
declare type Box<T> = {
42+
get: () => T;
43+
set: (value: T) => void;
44+
};
45+
declare function box<T>(value: T): Box<T>;
46+
declare const bn1: Box<number>;
47+
declare const bn2: Box<number>;
48+
declare const bb1: Box<boolean>;
49+
declare const bb2: Box<boolean>;
50+
interface Observable<T> {
51+
(): T;
52+
(value: T): any;
53+
}
54+
declare function observable<T>(value: T): Observable<T>;
55+
declare const x: Observable<boolean>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
=== tests/cases/compiler/contextuallyTypedBooleanLiterals.ts ===
2+
// Repro from #48363
3+
4+
type Box<T> = {
5+
>Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0))
6+
>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 2, 9))
7+
8+
get: () => T,
9+
>get : Symbol(get, Decl(contextuallyTypedBooleanLiterals.ts, 2, 15))
10+
>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 2, 9))
11+
12+
set: (value: T) => void
13+
>set : Symbol(set, Decl(contextuallyTypedBooleanLiterals.ts, 3, 17))
14+
>value : Symbol(value, Decl(contextuallyTypedBooleanLiterals.ts, 4, 10))
15+
>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 2, 9))
16+
}
17+
18+
declare function box<T>(value: T): Box<T>;
19+
>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 1))
20+
>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 7, 21))
21+
>value : Symbol(value, Decl(contextuallyTypedBooleanLiterals.ts, 7, 24))
22+
>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 7, 21))
23+
>Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0))
24+
>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 7, 21))
25+
26+
const bn1 = box(0); // Box<number>
27+
>bn1 : Symbol(bn1, Decl(contextuallyTypedBooleanLiterals.ts, 9, 5))
28+
>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 1))
29+
30+
const bn2: Box<number> = box(0); // Ok
31+
>bn2 : Symbol(bn2, Decl(contextuallyTypedBooleanLiterals.ts, 10, 5))
32+
>Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0))
33+
>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 1))
34+
35+
const bb1 = box(false); // Box<boolean>
36+
>bb1 : Symbol(bb1, Decl(contextuallyTypedBooleanLiterals.ts, 12, 5))
37+
>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 1))
38+
39+
const bb2: Box<boolean> = box(false); // Error, box<false> not assignable to Box<boolean>
40+
>bb2 : Symbol(bb2, Decl(contextuallyTypedBooleanLiterals.ts, 13, 5))
41+
>Box : Symbol(Box, Decl(contextuallyTypedBooleanLiterals.ts, 0, 0))
42+
>box : Symbol(box, Decl(contextuallyTypedBooleanLiterals.ts, 5, 1))
43+
44+
// Repro from #48150
45+
46+
interface Observable<T>
47+
>Observable : Symbol(Observable, Decl(contextuallyTypedBooleanLiterals.ts, 13, 37))
48+
>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 17, 21))
49+
{
50+
(): T;
51+
>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 17, 21))
52+
53+
(value: T): any;
54+
>value : Symbol(value, Decl(contextuallyTypedBooleanLiterals.ts, 20, 3))
55+
>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 17, 21))
56+
}
57+
58+
declare function observable<T>(value: T): Observable<T>;
59+
>observable : Symbol(observable, Decl(contextuallyTypedBooleanLiterals.ts, 21, 1))
60+
>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 23, 28))
61+
>value : Symbol(value, Decl(contextuallyTypedBooleanLiterals.ts, 23, 31))
62+
>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 23, 28))
63+
>Observable : Symbol(Observable, Decl(contextuallyTypedBooleanLiterals.ts, 13, 37))
64+
>T : Symbol(T, Decl(contextuallyTypedBooleanLiterals.ts, 23, 28))
65+
66+
const x: Observable<boolean> = observable(false);
67+
>x : Symbol(x, Decl(contextuallyTypedBooleanLiterals.ts, 25, 5))
68+
>Observable : Symbol(Observable, Decl(contextuallyTypedBooleanLiterals.ts, 13, 37))
69+
>observable : Symbol(observable, Decl(contextuallyTypedBooleanLiterals.ts, 21, 1))
70+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
=== tests/cases/compiler/contextuallyTypedBooleanLiterals.ts ===
2+
// Repro from #48363
3+
4+
type Box<T> = {
5+
>Box : Box<T>
6+
7+
get: () => T,
8+
>get : () => T
9+
10+
set: (value: T) => void
11+
>set : (value: T) => void
12+
>value : T
13+
}
14+
15+
declare function box<T>(value: T): Box<T>;
16+
>box : <T>(value: T) => Box<T>
17+
>value : T
18+
19+
const bn1 = box(0); // Box<number>
20+
>bn1 : Box<number>
21+
>box(0) : Box<number>
22+
>box : <T>(value: T) => Box<T>
23+
>0 : 0
24+
25+
const bn2: Box<number> = box(0); // Ok
26+
>bn2 : Box<number>
27+
>box(0) : Box<number>
28+
>box : <T>(value: T) => Box<T>
29+
>0 : 0
30+
31+
const bb1 = box(false); // Box<boolean>
32+
>bb1 : Box<boolean>
33+
>box(false) : Box<boolean>
34+
>box : <T>(value: T) => Box<T>
35+
>false : false
36+
37+
const bb2: Box<boolean> = box(false); // Error, box<false> not assignable to Box<boolean>
38+
>bb2 : Box<boolean>
39+
>box(false) : Box<boolean>
40+
>box : <T>(value: T) => Box<T>
41+
>false : false
42+
43+
// Repro from #48150
44+
45+
interface Observable<T>
46+
{
47+
(): T;
48+
(value: T): any;
49+
>value : T
50+
}
51+
52+
declare function observable<T>(value: T): Observable<T>;
53+
>observable : <T>(value: T) => Observable<T>
54+
>value : T
55+
56+
const x: Observable<boolean> = observable(false);
57+
>x : Observable<boolean>
58+
>observable(false) : Observable<boolean>
59+
>observable : <T>(value: T) => Observable<T>
60+
>false : false
61+

Diff for: tests/baselines/reference/inferFromGenericFunctionReturnTypes3.errors.txt

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
tests/cases/compiler/inferFromGenericFunctionReturnTypes3.ts(28,30): error TS2345: Argument of type 'string' is not assignable to parameter of type '"bar"'.
2-
tests/cases/compiler/inferFromGenericFunctionReturnTypes3.ts(175,47): error TS2322: Type 'boolean' is not assignable to type 'true'.
32
tests/cases/compiler/inferFromGenericFunctionReturnTypes3.ts(180,26): error TS2322: Type '{ state: State.A; }[] | { state: State.B; }[]' is not assignable to type '{ state: State.A; }[]'.
43
Type '{ state: State.B; }[]' is not assignable to type '{ state: State.A; }[]'.
54
Type '{ state: State.B; }' is not assignable to type '{ state: State.A; }'.
65
Types of property 'state' are incompatible.
76
Type 'State.B' is not assignable to type 'State.A'.
87

98

10-
==== tests/cases/compiler/inferFromGenericFunctionReturnTypes3.ts (3 errors) ====
9+
==== tests/cases/compiler/inferFromGenericFunctionReturnTypes3.ts (2 errors) ====
1110
// Repros from #5487
1211

1312
function truePromise(): Promise<true> {
@@ -185,9 +184,6 @@ tests/cases/compiler/inferFromGenericFunctionReturnTypes3.ts(180,26): error TS23
185184

186185
declare function foldLeft<U>(z: U, f: (acc: U, t: boolean) => U): U;
187186
let res: boolean = foldLeft(true, (acc, t) => acc && t); // Error
188-
~~~~~~~~
189-
!!! error TS2322: Type 'boolean' is not assignable to type 'true'.
190-
!!! related TS6502 tests/cases/compiler/inferFromGenericFunctionReturnTypes3.ts:174:39: The expected type comes from the return type of this signature.
191187

192188
enum State { A, B }
193189
type Foo = { state: State }

Diff for: tests/baselines/reference/inferFromGenericFunctionReturnTypes3.types

+4-4
Original file line numberDiff line numberDiff line change
@@ -461,14 +461,14 @@ declare function foldLeft<U>(z: U, f: (acc: U, t: boolean) => U): U;
461461

462462
let res: boolean = foldLeft(true, (acc, t) => acc && t); // Error
463463
>res : boolean
464-
>foldLeft(true, (acc, t) => acc && t) : true
464+
>foldLeft(true, (acc, t) => acc && t) : boolean
465465
>foldLeft : <U>(z: U, f: (acc: U, t: boolean) => U) => U
466466
>true : true
467-
>(acc, t) => acc && t : (acc: true, t: boolean) => boolean
468-
>acc : true
467+
>(acc, t) => acc && t : (acc: boolean, t: boolean) => boolean
468+
>acc : boolean
469469
>t : boolean
470470
>acc && t : boolean
471-
>acc : true
471+
>acc : boolean
472472
>t : boolean
473473

474474
enum State { A, B }
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// @strict: true
2+
// @declaration: true
3+
4+
// @strict: true
5+
// @declaration: true
6+
7+
// Repro from #48363
8+
9+
type Box<T> = {
10+
get: () => T,
11+
set: (value: T) => void
12+
}
13+
14+
declare function box<T>(value: T): Box<T>;
15+
16+
const bn1 = box(0); // Box<number>
17+
const bn2: Box<number> = box(0); // Ok
18+
19+
const bb1 = box(false); // Box<boolean>
20+
const bb2: Box<boolean> = box(false); // Error, box<false> not assignable to Box<boolean>
21+
22+
// Repro from #48150
23+
24+
interface Observable<T>
25+
{
26+
(): T;
27+
(value: T): any;
28+
}
29+
30+
declare function observable<T>(value: T): Observable<T>;
31+
32+
const x: Observable<boolean> = observable(false);

0 commit comments

Comments
 (0)