Skip to content

Commit a668b6b

Browse files
committed
allow extends type to be supertype of constraint types
1 parent c50b5e7 commit a668b6b

File tree

6 files changed

+309
-9
lines changed

6 files changed

+309
-9
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46082,7 +46082,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4608246082
// (0) The conditional type is distributive;
4608346083
// (1) The conditional type has no `infer` type parameters;
4608446084
// (2) The conditional type's check type is a narrowable type parameter (i.e. a type parameter with a union constraint);
46085-
// (3) The extends type `A` is a type or a union of types belonging to the union constraint of the type parameter;
46085+
// (3) The extends type `A` is a type or a union of types that are supertypes of the union constraint of the type parameter;
4608646086
// (4) `TrueBranch<T>` and `FalseBranch<T>` must be valid, recursively.
4608746087
// In particular, the false-most branch of the conditional type must be `never`.
4608846088
function isNarrowableConditionalTypeWorker(type: ConditionalType): boolean {
@@ -46111,7 +46111,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4611146111
!everyType(type.extendsType, extendsType =>
4611246112
some(
4611346113
(constraintType as UnionType).types,
46114-
constraintType => isTypeIdenticalTo(constraintType, extendsType),
46114+
constraintType => isTypeAssignableTo(constraintType, extendsType),
4611546115
))
4611646116
) {
4611746117
return false;

tests/baselines/reference/dependentReturnType1.errors.txt

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ dependentReturnType1.ts(275,9): error TS2322: Type '1' is not assignable to type
2626
dependentReturnType1.ts(278,9): error TS2322: Type '2' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'.
2727
dependentReturnType1.ts(280,5): error TS2322: Type '0' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'.
2828
dependentReturnType1.ts(302,9): error TS2322: Type 'string' is not assignable to type 'string[]'.
29-
dependentReturnType1.ts(311,9): error TS2322: Type 'undefined' is not assignable to type 'T extends {} ? void : T extends undefined ? number : never'.
30-
dependentReturnType1.ts(313,5): error TS2322: Type 'number' is not assignable to type 'T extends {} ? void : T extends undefined ? number : never'.
3129
dependentReturnType1.ts(334,9): error TS2322: Type '1' is not assignable to type '4'.
3230
dependentReturnType1.ts(367,13): error TS2322: Type 'number' is not assignable to type 'T extends 1 ? number : T extends 2 ? string : never'.
3331
dependentReturnType1.ts(369,9): error TS2322: Type 'string' is not assignable to type 'T extends 1 ? number : T extends 2 ? string : never'.
@@ -44,7 +42,7 @@ dependentReturnType1.ts(488,9): error TS2322: Type 'R' is not assignable to type
4442
dependentReturnType1.ts(514,5): error TS2322: Type '1' is not assignable to type 'never'.
4543

4644

47-
==== dependentReturnType1.ts (39 errors) ====
45+
==== dependentReturnType1.ts (37 errors) ====
4846
interface A {
4947
1: number;
5048
2: string;
@@ -407,12 +405,8 @@ dependentReturnType1.ts(514,5): error TS2322: Type '1' is not assignable to type
407405
): T extends {} ? void : T extends undefined ? number : never {
408406
if (x) {
409407
return;
410-
~~~~~~
411-
!!! error TS2322: Type 'undefined' is not assignable to type 'T extends {} ? void : T extends undefined ? number : never'.
412408
}
413409
return 1;
414-
~~~~~~
415-
!!! error TS2322: Type 'number' is not assignable to type 'T extends {} ? void : T extends undefined ? number : never'.
416410
}
417411

418412
// Multiple type parameters at once
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
dependentReturnType10.ts(29,9): error TS2322: Type 'number' is not assignable to type 'void'.
2+
3+
4+
==== dependentReturnType10.ts (1 errors) ====
5+
interface Animal {
6+
name: string;
7+
species: string;
8+
}
9+
10+
interface Dog extends Animal {
11+
breed: string;
12+
}
13+
14+
type GreetRet<T> =
15+
T extends string ? string :
16+
T extends { name: string } ? { greeting: string, breed: string } :
17+
never;
18+
19+
function greet<T extends string | Dog>(animal: T): GreetRet<T> {
20+
if (typeof animal === "string") {
21+
return `hello, ${animal}`
22+
}
23+
return { greeting: `woof, ${animal.name}`, breed: animal.breed }
24+
}
25+
26+
type BadRet<T> =
27+
T extends {} ? void :
28+
T extends string ? number :
29+
never;
30+
31+
function badFun<T extends { a: string } | string>(x: T): BadRet<T> {
32+
if (typeof x === "string") {
33+
return 1
34+
~~~~~~
35+
!!! error TS2322: Type 'number' is not assignable to type 'void'.
36+
}
37+
return;
38+
}
39+
40+
declare let arg2: { a: string } | string;
41+
const badRet = badFun(arg2);
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//// [tests/cases/compiler/dependentReturnType10.ts] ////
2+
3+
=== dependentReturnType10.ts ===
4+
interface Animal {
5+
>Animal : Symbol(Animal, Decl(dependentReturnType10.ts, 0, 0))
6+
7+
name: string;
8+
>name : Symbol(Animal.name, Decl(dependentReturnType10.ts, 0, 18))
9+
10+
species: string;
11+
>species : Symbol(Animal.species, Decl(dependentReturnType10.ts, 1, 17))
12+
}
13+
14+
interface Dog extends Animal {
15+
>Dog : Symbol(Dog, Decl(dependentReturnType10.ts, 3, 1))
16+
>Animal : Symbol(Animal, Decl(dependentReturnType10.ts, 0, 0))
17+
18+
breed: string;
19+
>breed : Symbol(Dog.breed, Decl(dependentReturnType10.ts, 5, 30))
20+
}
21+
22+
type GreetRet<T> =
23+
>GreetRet : Symbol(GreetRet, Decl(dependentReturnType10.ts, 7, 1))
24+
>T : Symbol(T, Decl(dependentReturnType10.ts, 9, 14))
25+
26+
T extends string ? string :
27+
>T : Symbol(T, Decl(dependentReturnType10.ts, 9, 14))
28+
29+
T extends { name: string } ? { greeting: string, breed: string } :
30+
>T : Symbol(T, Decl(dependentReturnType10.ts, 9, 14))
31+
>name : Symbol(name, Decl(dependentReturnType10.ts, 11, 15))
32+
>greeting : Symbol(greeting, Decl(dependentReturnType10.ts, 11, 34))
33+
>breed : Symbol(breed, Decl(dependentReturnType10.ts, 11, 52))
34+
35+
never;
36+
37+
function greet<T extends string | Dog>(animal: T): GreetRet<T> {
38+
>greet : Symbol(greet, Decl(dependentReturnType10.ts, 12, 10))
39+
>T : Symbol(T, Decl(dependentReturnType10.ts, 14, 15))
40+
>Dog : Symbol(Dog, Decl(dependentReturnType10.ts, 3, 1))
41+
>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39))
42+
>T : Symbol(T, Decl(dependentReturnType10.ts, 14, 15))
43+
>GreetRet : Symbol(GreetRet, Decl(dependentReturnType10.ts, 7, 1))
44+
>T : Symbol(T, Decl(dependentReturnType10.ts, 14, 15))
45+
46+
if (typeof animal === "string") {
47+
>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39))
48+
49+
return `hello, ${animal}`
50+
>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39))
51+
}
52+
return { greeting: `woof, ${animal.name}`, breed: animal.breed }
53+
>greeting : Symbol(greeting, Decl(dependentReturnType10.ts, 18, 12))
54+
>animal.name : Symbol(Animal.name, Decl(dependentReturnType10.ts, 0, 18))
55+
>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39))
56+
>name : Symbol(Animal.name, Decl(dependentReturnType10.ts, 0, 18))
57+
>breed : Symbol(breed, Decl(dependentReturnType10.ts, 18, 46))
58+
>animal.breed : Symbol(Dog.breed, Decl(dependentReturnType10.ts, 5, 30))
59+
>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39))
60+
>breed : Symbol(Dog.breed, Decl(dependentReturnType10.ts, 5, 30))
61+
}
62+
63+
type BadRet<T> =
64+
>BadRet : Symbol(BadRet, Decl(dependentReturnType10.ts, 19, 1))
65+
>T : Symbol(T, Decl(dependentReturnType10.ts, 21, 12))
66+
67+
T extends {} ? void :
68+
>T : Symbol(T, Decl(dependentReturnType10.ts, 21, 12))
69+
70+
T extends string ? number :
71+
>T : Symbol(T, Decl(dependentReturnType10.ts, 21, 12))
72+
73+
never;
74+
75+
function badFun<T extends { a: string } | string>(x: T): BadRet<T> {
76+
>badFun : Symbol(badFun, Decl(dependentReturnType10.ts, 24, 10))
77+
>T : Symbol(T, Decl(dependentReturnType10.ts, 26, 16))
78+
>a : Symbol(a, Decl(dependentReturnType10.ts, 26, 27))
79+
>x : Symbol(x, Decl(dependentReturnType10.ts, 26, 50))
80+
>T : Symbol(T, Decl(dependentReturnType10.ts, 26, 16))
81+
>BadRet : Symbol(BadRet, Decl(dependentReturnType10.ts, 19, 1))
82+
>T : Symbol(T, Decl(dependentReturnType10.ts, 26, 16))
83+
84+
if (typeof x === "string") {
85+
>x : Symbol(x, Decl(dependentReturnType10.ts, 26, 50))
86+
87+
return 1
88+
}
89+
return;
90+
}
91+
92+
declare let arg2: { a: string } | string;
93+
>arg2 : Symbol(arg2, Decl(dependentReturnType10.ts, 33, 11))
94+
>a : Symbol(a, Decl(dependentReturnType10.ts, 33, 19))
95+
96+
const badRet = badFun(arg2);
97+
>badRet : Symbol(badRet, Decl(dependentReturnType10.ts, 34, 5))
98+
>badFun : Symbol(badFun, Decl(dependentReturnType10.ts, 24, 10))
99+
>arg2 : Symbol(arg2, Decl(dependentReturnType10.ts, 33, 11))
100+
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
//// [tests/cases/compiler/dependentReturnType10.ts] ////
2+
3+
=== dependentReturnType10.ts ===
4+
interface Animal {
5+
name: string;
6+
>name : string
7+
> : ^^^^^^
8+
9+
species: string;
10+
>species : string
11+
> : ^^^^^^
12+
}
13+
14+
interface Dog extends Animal {
15+
breed: string;
16+
>breed : string
17+
> : ^^^^^^
18+
}
19+
20+
type GreetRet<T> =
21+
>GreetRet : GreetRet<T>
22+
> : ^^^^^^^^^^^
23+
24+
T extends string ? string :
25+
T extends { name: string } ? { greeting: string, breed: string } :
26+
>name : string
27+
> : ^^^^^^
28+
>greeting : string
29+
> : ^^^^^^
30+
>breed : string
31+
> : ^^^^^^
32+
33+
never;
34+
35+
function greet<T extends string | Dog>(animal: T): GreetRet<T> {
36+
>greet : <T extends string | Dog>(animal: T) => GreetRet<T>
37+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
38+
>animal : T
39+
> : ^
40+
41+
if (typeof animal === "string") {
42+
>typeof animal === "string" : boolean
43+
> : ^^^^^^^
44+
>typeof animal : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
45+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
46+
>animal : T
47+
> : ^
48+
>"string" : "string"
49+
> : ^^^^^^^^
50+
51+
return `hello, ${animal}`
52+
>`hello, ${animal}` : string
53+
> : ^^^^^^
54+
>animal : T & string
55+
> : ^^^^^^^^^^
56+
}
57+
return { greeting: `woof, ${animal.name}`, breed: animal.breed }
58+
>{ greeting: `woof, ${animal.name}`, breed: animal.breed } : { greeting: string; breed: string; }
59+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
60+
>greeting : string
61+
> : ^^^^^^
62+
>`woof, ${animal.name}` : string
63+
> : ^^^^^^
64+
>animal.name : string
65+
> : ^^^^^^
66+
>animal : Dog
67+
> : ^^^
68+
>name : string
69+
> : ^^^^^^
70+
>breed : string
71+
> : ^^^^^^
72+
>animal.breed : string
73+
> : ^^^^^^
74+
>animal : Dog
75+
> : ^^^
76+
>breed : string
77+
> : ^^^^^^
78+
}
79+
80+
type BadRet<T> =
81+
>BadRet : BadRet<T>
82+
> : ^^^^^^^^^
83+
84+
T extends {} ? void :
85+
T extends string ? number :
86+
never;
87+
88+
function badFun<T extends { a: string } | string>(x: T): BadRet<T> {
89+
>badFun : <T extends { a: string; } | string>(x: T) => BadRet<T>
90+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
91+
>a : string
92+
> : ^^^^^^
93+
>x : T
94+
> : ^
95+
96+
if (typeof x === "string") {
97+
>typeof x === "string" : boolean
98+
> : ^^^^^^^
99+
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
100+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
101+
>x : T
102+
> : ^
103+
>"string" : "string"
104+
> : ^^^^^^^^
105+
106+
return 1
107+
>1 : 1
108+
> : ^
109+
}
110+
return;
111+
}
112+
113+
declare let arg2: { a: string } | string;
114+
>arg2 : string | { a: string; }
115+
> : ^^^^^^^^^^^^^^ ^^^
116+
>a : string
117+
> : ^^^^^^
118+
119+
const badRet = badFun(arg2);
120+
>badRet : void
121+
> : ^^^^
122+
>badFun(arg2) : void
123+
> : ^^^^
124+
>badFun : <T extends { a: string; } | string>(x: T) => BadRet<T>
125+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
126+
>arg2 : string | { a: string; }
127+
> : ^^^^^^^^^^^^^^ ^^^
128+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// @noEmit: true
2+
3+
interface Animal {
4+
name: string;
5+
species: string;
6+
}
7+
8+
interface Dog extends Animal {
9+
breed: string;
10+
}
11+
12+
type GreetRet<T> =
13+
T extends string ? string :
14+
T extends { name: string } ? { greeting: string, breed: string } :
15+
never;
16+
17+
function greet<T extends string | Dog>(animal: T): GreetRet<T> {
18+
if (typeof animal === "string") {
19+
return `hello, ${animal}`
20+
}
21+
return { greeting: `woof, ${animal.name}`, breed: animal.breed }
22+
}
23+
24+
type BadRet<T> =
25+
T extends {} ? void :
26+
T extends string ? number :
27+
never;
28+
29+
function badFun<T extends { a: string } | string>(x: T): BadRet<T> {
30+
if (typeof x === "string") {
31+
return 1
32+
}
33+
return;
34+
}
35+
36+
declare let arg2: { a: string } | string;
37+
const badRet = badFun(arg2);

0 commit comments

Comments
 (0)