relax restriction on extends type of narrowable conditional type

This commit is contained in:
Gabriela Araujo Britto 2025-02-01 12:34:04 -08:00
parent e0d466dcca
commit a3e7ea503d
4 changed files with 163 additions and 2 deletions

View File

@ -46100,7 +46100,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<T>` and `FalseBranch<T>` must be valid, recursively.
// In particular, the false-most branch of the conditional type must be `never`.
function isNarrowableConditionalTypeWorker(type: ConditionalType): boolean {
@ -46129,7 +46129,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;

View File

@ -0,0 +1,61 @@
//// [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<T> =
>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<T extends string | Dog>(animal: T): GreetRet<T> {
>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))
}

View File

@ -0,0 +1,78 @@
//// [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<T> =
>GreetRet : GreetRet<T>
> : ^^^^^^^^^^^
T extends string ? string :
T extends { name: string } ? { greeting: string, breed: string } :
>name : string
> : ^^^^^^
>greeting : string
> : ^^^^^^
>breed : string
> : ^^^^^^
never;
function greet<T extends string | Dog>(animal: T): GreetRet<T> {
>greet : <T extends string | Dog>(animal: T) => GreetRet<T>
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>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
> : ^^^^^^
}

View File

@ -0,0 +1,22 @@
// @noEmit: true
interface Animal {
name: string;
species: string;
}
interface Dog extends Animal {
breed: string;
}
type GreetRet<T> =
T extends string ? string :
T extends { name: string } ? { greeting: string, breed: string } :
never;
function greet<T extends string | Dog>(animal: T): GreetRet<T> {
if (typeof animal === "string") {
return `hello, ${animal}`
}
return { greeting: `woof, ${animal.name}`, breed: animal.breed }
}