diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fcb93c45dc1..b1b5dacd450 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -46082,7 +46082,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // (0) The conditional type is distributive; // (1) The conditional type has no `infer` type parameters; // (2) The conditional type's check type is a narrowable type parameter (i.e. a type parameter with a union constraint); - // (3) The extends type `A` is a type or a union of types belonging to the union constraint of the type parameter; + // (3) The extends type `A` is a type or a union of types that are supertypes of the union constraint of the type parameter; // (4) `TrueBranch` and `FalseBranch` must be valid, recursively. // In particular, the false-most branch of the conditional type must be `never`. function isNarrowableConditionalTypeWorker(type: ConditionalType): boolean { @@ -46111,7 +46111,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { !everyType(type.extendsType, extendsType => some( (constraintType as UnionType).types, - constraintType => isTypeIdenticalTo(constraintType, extendsType), + constraintType => isTypeAssignableTo(constraintType, extendsType), )) ) { return false; diff --git a/tests/baselines/reference/dependentReturnType1.errors.txt b/tests/baselines/reference/dependentReturnType1.errors.txt index 4513ccb75cf..eaffd76cfba 100644 --- a/tests/baselines/reference/dependentReturnType1.errors.txt +++ b/tests/baselines/reference/dependentReturnType1.errors.txt @@ -26,8 +26,6 @@ dependentReturnType1.ts(275,9): error TS2322: Type '1' is not assignable to type 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>'. 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>'. dependentReturnType1.ts(302,9): error TS2322: Type 'string' is not assignable to type 'string[]'. -dependentReturnType1.ts(311,9): error TS2322: Type 'undefined' is not assignable to type 'T extends {} ? void : T extends undefined ? number : never'. -dependentReturnType1.ts(313,5): error TS2322: Type 'number' is not assignable to type 'T extends {} ? void : T extends undefined ? number : never'. dependentReturnType1.ts(334,9): error TS2322: Type '1' is not assignable to type '4'. dependentReturnType1.ts(367,13): error TS2322: Type 'number' is not assignable to type 'T extends 1 ? number : T extends 2 ? string : never'. 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 dependentReturnType1.ts(514,5): error TS2322: Type '1' is not assignable to type 'never'. -==== dependentReturnType1.ts (39 errors) ==== +==== dependentReturnType1.ts (37 errors) ==== interface A { 1: number; 2: string; @@ -407,12 +405,8 @@ dependentReturnType1.ts(514,5): error TS2322: Type '1' is not assignable to type ): T extends {} ? void : T extends undefined ? number : never { if (x) { return; - ~~~~~~ -!!! error TS2322: Type 'undefined' is not assignable to type 'T extends {} ? void : T extends undefined ? number : never'. } return 1; - ~~~~~~ -!!! error TS2322: Type 'number' is not assignable to type 'T extends {} ? void : T extends undefined ? number : never'. } // Multiple type parameters at once diff --git a/tests/baselines/reference/dependentReturnType10.errors.txt b/tests/baselines/reference/dependentReturnType10.errors.txt new file mode 100644 index 00000000000..c4f2d23efaf --- /dev/null +++ b/tests/baselines/reference/dependentReturnType10.errors.txt @@ -0,0 +1,41 @@ +dependentReturnType10.ts(29,9): error TS2322: Type 'number' is not assignable to type 'void'. + + +==== dependentReturnType10.ts (1 errors) ==== + interface Animal { + name: string; + species: string; + } + + interface Dog extends Animal { + breed: string; + } + + type GreetRet = + T extends string ? string : + T extends { name: string } ? { greeting: string, breed: string } : + never; + + function greet(animal: T): GreetRet { + if (typeof animal === "string") { + return `hello, ${animal}` + } + return { greeting: `woof, ${animal.name}`, breed: animal.breed } + } + + type BadRet = + T extends {} ? void : + T extends string ? number : + never; + + function badFun(x: T): BadRet { + if (typeof x === "string") { + return 1 + ~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'void'. + } + return; + } + + declare let arg2: { a: string } | string; + const badRet = badFun(arg2); \ No newline at end of file diff --git a/tests/baselines/reference/dependentReturnType10.symbols b/tests/baselines/reference/dependentReturnType10.symbols new file mode 100644 index 00000000000..6715843da1b --- /dev/null +++ b/tests/baselines/reference/dependentReturnType10.symbols @@ -0,0 +1,100 @@ +//// [tests/cases/compiler/dependentReturnType10.ts] //// + +=== dependentReturnType10.ts === +interface Animal { +>Animal : Symbol(Animal, Decl(dependentReturnType10.ts, 0, 0)) + + name: string; +>name : Symbol(Animal.name, Decl(dependentReturnType10.ts, 0, 18)) + + species: string; +>species : Symbol(Animal.species, Decl(dependentReturnType10.ts, 1, 17)) +} + +interface Dog extends Animal { +>Dog : Symbol(Dog, Decl(dependentReturnType10.ts, 3, 1)) +>Animal : Symbol(Animal, Decl(dependentReturnType10.ts, 0, 0)) + + breed: string; +>breed : Symbol(Dog.breed, Decl(dependentReturnType10.ts, 5, 30)) +} + +type GreetRet = +>GreetRet : Symbol(GreetRet, Decl(dependentReturnType10.ts, 7, 1)) +>T : Symbol(T, Decl(dependentReturnType10.ts, 9, 14)) + + T extends string ? string : +>T : Symbol(T, Decl(dependentReturnType10.ts, 9, 14)) + + T extends { name: string } ? { greeting: string, breed: string } : +>T : Symbol(T, Decl(dependentReturnType10.ts, 9, 14)) +>name : Symbol(name, Decl(dependentReturnType10.ts, 11, 15)) +>greeting : Symbol(greeting, Decl(dependentReturnType10.ts, 11, 34)) +>breed : Symbol(breed, Decl(dependentReturnType10.ts, 11, 52)) + + never; + +function greet(animal: T): GreetRet { +>greet : Symbol(greet, Decl(dependentReturnType10.ts, 12, 10)) +>T : Symbol(T, Decl(dependentReturnType10.ts, 14, 15)) +>Dog : Symbol(Dog, Decl(dependentReturnType10.ts, 3, 1)) +>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39)) +>T : Symbol(T, Decl(dependentReturnType10.ts, 14, 15)) +>GreetRet : Symbol(GreetRet, Decl(dependentReturnType10.ts, 7, 1)) +>T : Symbol(T, Decl(dependentReturnType10.ts, 14, 15)) + + if (typeof animal === "string") { +>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39)) + + return `hello, ${animal}` +>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39)) + } + return { greeting: `woof, ${animal.name}`, breed: animal.breed } +>greeting : Symbol(greeting, Decl(dependentReturnType10.ts, 18, 12)) +>animal.name : Symbol(Animal.name, Decl(dependentReturnType10.ts, 0, 18)) +>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39)) +>name : Symbol(Animal.name, Decl(dependentReturnType10.ts, 0, 18)) +>breed : Symbol(breed, Decl(dependentReturnType10.ts, 18, 46)) +>animal.breed : Symbol(Dog.breed, Decl(dependentReturnType10.ts, 5, 30)) +>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39)) +>breed : Symbol(Dog.breed, Decl(dependentReturnType10.ts, 5, 30)) +} + +type BadRet = +>BadRet : Symbol(BadRet, Decl(dependentReturnType10.ts, 19, 1)) +>T : Symbol(T, Decl(dependentReturnType10.ts, 21, 12)) + + T extends {} ? void : +>T : Symbol(T, Decl(dependentReturnType10.ts, 21, 12)) + + T extends string ? number : +>T : Symbol(T, Decl(dependentReturnType10.ts, 21, 12)) + + never; + +function badFun(x: T): BadRet { +>badFun : Symbol(badFun, Decl(dependentReturnType10.ts, 24, 10)) +>T : Symbol(T, Decl(dependentReturnType10.ts, 26, 16)) +>a : Symbol(a, Decl(dependentReturnType10.ts, 26, 27)) +>x : Symbol(x, Decl(dependentReturnType10.ts, 26, 50)) +>T : Symbol(T, Decl(dependentReturnType10.ts, 26, 16)) +>BadRet : Symbol(BadRet, Decl(dependentReturnType10.ts, 19, 1)) +>T : Symbol(T, Decl(dependentReturnType10.ts, 26, 16)) + + if (typeof x === "string") { +>x : Symbol(x, Decl(dependentReturnType10.ts, 26, 50)) + + return 1 + } + return; +} + +declare let arg2: { a: string } | string; +>arg2 : Symbol(arg2, Decl(dependentReturnType10.ts, 33, 11)) +>a : Symbol(a, Decl(dependentReturnType10.ts, 33, 19)) + +const badRet = badFun(arg2); +>badRet : Symbol(badRet, Decl(dependentReturnType10.ts, 34, 5)) +>badFun : Symbol(badFun, Decl(dependentReturnType10.ts, 24, 10)) +>arg2 : Symbol(arg2, Decl(dependentReturnType10.ts, 33, 11)) + diff --git a/tests/baselines/reference/dependentReturnType10.types b/tests/baselines/reference/dependentReturnType10.types new file mode 100644 index 00000000000..bc813c28552 --- /dev/null +++ b/tests/baselines/reference/dependentReturnType10.types @@ -0,0 +1,128 @@ +//// [tests/cases/compiler/dependentReturnType10.ts] //// + +=== dependentReturnType10.ts === +interface Animal { + name: string; +>name : string +> : ^^^^^^ + + species: string; +>species : string +> : ^^^^^^ +} + +interface Dog extends Animal { + breed: string; +>breed : string +> : ^^^^^^ +} + +type GreetRet = +>GreetRet : GreetRet +> : ^^^^^^^^^^^ + + T extends string ? string : + T extends { name: string } ? { greeting: string, breed: string } : +>name : string +> : ^^^^^^ +>greeting : string +> : ^^^^^^ +>breed : string +> : ^^^^^^ + + never; + +function greet(animal: T): GreetRet { +>greet : (animal: T) => GreetRet +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>animal : T +> : ^ + + if (typeof animal === "string") { +>typeof animal === "string" : boolean +> : ^^^^^^^ +>typeof animal : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>animal : T +> : ^ +>"string" : "string" +> : ^^^^^^^^ + + return `hello, ${animal}` +>`hello, ${animal}` : string +> : ^^^^^^ +>animal : T & string +> : ^^^^^^^^^^ + } + return { greeting: `woof, ${animal.name}`, breed: animal.breed } +>{ greeting: `woof, ${animal.name}`, breed: animal.breed } : { greeting: string; breed: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>greeting : string +> : ^^^^^^ +>`woof, ${animal.name}` : string +> : ^^^^^^ +>animal.name : string +> : ^^^^^^ +>animal : Dog +> : ^^^ +>name : string +> : ^^^^^^ +>breed : string +> : ^^^^^^ +>animal.breed : string +> : ^^^^^^ +>animal : Dog +> : ^^^ +>breed : string +> : ^^^^^^ +} + +type BadRet = +>BadRet : BadRet +> : ^^^^^^^^^ + + T extends {} ? void : + T extends string ? number : + never; + +function badFun(x: T): BadRet { +>badFun : (x: T) => BadRet +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>a : string +> : ^^^^^^ +>x : T +> : ^ + + if (typeof x === "string") { +>typeof x === "string" : boolean +> : ^^^^^^^ +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>x : T +> : ^ +>"string" : "string" +> : ^^^^^^^^ + + return 1 +>1 : 1 +> : ^ + } + return; +} + +declare let arg2: { a: string } | string; +>arg2 : string | { a: string; } +> : ^^^^^^^^^^^^^^ ^^^ +>a : string +> : ^^^^^^ + +const badRet = badFun(arg2); +>badRet : void +> : ^^^^ +>badFun(arg2) : void +> : ^^^^ +>badFun : (x: T) => BadRet +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>arg2 : string | { a: string; } +> : ^^^^^^^^^^^^^^ ^^^ + diff --git a/tests/cases/compiler/dependentReturnType10.ts b/tests/cases/compiler/dependentReturnType10.ts new file mode 100644 index 00000000000..4ada38a97dd --- /dev/null +++ b/tests/cases/compiler/dependentReturnType10.ts @@ -0,0 +1,37 @@ +// @noEmit: true + +interface Animal { + name: string; + species: string; +} + +interface Dog extends Animal { + breed: string; +} + +type GreetRet = + T extends string ? string : + T extends { name: string } ? { greeting: string, breed: string } : + never; + +function greet(animal: T): GreetRet { + if (typeof animal === "string") { + return `hello, ${animal}` + } + return { greeting: `woof, ${animal.name}`, breed: animal.breed } +} + +type BadRet = + T extends {} ? void : + T extends string ? number : + never; + +function badFun(x: T): BadRet { + if (typeof x === "string") { + return 1 + } + return; +} + +declare let arg2: { a: string } | string; +const badRet = badFun(arg2); \ No newline at end of file