mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Hoist and distribute type parameter constraints over type parameters … (#33453)
* Hoist and distribute type parameter constraints over type parameters when comparing against union targets when fetching union constraints * Fix PR nits
This commit is contained in:
parent
26caa3793e
commit
367b82055c
@ -7965,10 +7965,10 @@ namespace ts {
|
||||
return hasNonCircularBaseConstraint(type) ? getConstraintFromConditionalType(type) : undefined;
|
||||
}
|
||||
|
||||
function getUnionConstraintOfIntersection(type: IntersectionType, targetIsUnion: boolean) {
|
||||
function getEffectiveConstraintOfIntersection(types: readonly Type[], targetIsUnion: boolean) {
|
||||
let constraints: Type[] | undefined;
|
||||
let hasDisjointDomainType = false;
|
||||
for (const t of type.types) {
|
||||
for (const t of types) {
|
||||
if (t.flags & TypeFlags.Instantiable) {
|
||||
// We keep following constraints as long as we have an instantiable type that is known
|
||||
// not to be circular or infinite (hence we stop on index access types).
|
||||
@ -7978,6 +7978,9 @@ namespace ts {
|
||||
}
|
||||
if (constraint) {
|
||||
constraints = append(constraints, constraint);
|
||||
if (targetIsUnion) {
|
||||
constraints = append(constraints, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (t.flags & TypeFlags.DisjointDomains) {
|
||||
@ -7990,7 +7993,7 @@ namespace ts {
|
||||
if (hasDisjointDomainType) {
|
||||
// We add any types belong to one of the disjoint domains because they might cause the final
|
||||
// intersection operation to reduce the union constraints.
|
||||
for (const t of type.types) {
|
||||
for (const t of types) {
|
||||
if (t.flags & TypeFlags.DisjointDomains) {
|
||||
constraints = append(constraints, t);
|
||||
}
|
||||
@ -13089,7 +13092,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!result && source.flags & TypeFlags.Intersection) {
|
||||
if (!result && source.flags & (TypeFlags.Intersection | TypeFlags.TypeParameter)) {
|
||||
// The combined constraint of an intersection type is the intersection of the constraints of
|
||||
// the constituents. When an intersection type contains instantiable types with union type
|
||||
// constraints, there are situations where we need to examine the combined constraint. One is
|
||||
@ -13099,10 +13102,17 @@ namespace ts {
|
||||
// we need to check this constraint against a union on the target side. Also, given a type
|
||||
// variable V constrained to 'string | number', 'V & number' has a combined constraint of
|
||||
// 'string & number | number & number' which reduces to just 'number'.
|
||||
const constraint = getUnionConstraintOfIntersection(<IntersectionType>source, !!(target.flags & TypeFlags.Union));
|
||||
if (constraint) {
|
||||
if (result = isRelatedTo(constraint, target, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent)) {
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
// This also handles type parameters, as a type parameter with a union constraint compared against a union
|
||||
// needs to have its constraint hoisted into an intersection with said type parameter, this way
|
||||
// the type param can be compared with itself in the target (with the influence of its constraint to match other parts)
|
||||
// For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)`
|
||||
const constraint = getEffectiveConstraintOfIntersection(source.flags & TypeFlags.Intersection ? (<IntersectionType>source).types: [source], !!(target.flags & TypeFlags.Union));
|
||||
if (constraint && (source.flags & TypeFlags.Intersection || target.flags & TypeFlags.Union)) {
|
||||
if (everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself
|
||||
// TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this
|
||||
if (result = isRelatedTo(constraint, target, /*reportErrors*/ false, /*headMessage*/ undefined, isIntersectionConstituent)) {
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +1,13 @@
|
||||
tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(7,9): error TS2322: Type 'T & U' is not assignable to type 'string | number'.
|
||||
Type 'string | undefined' is not assignable to type 'string | number'.
|
||||
Type 'undefined' is not assignable to type 'string | number'.
|
||||
Type 'T & U' is not assignable to type 'number'.
|
||||
Type 'T & U' is not assignable to type 'number'.
|
||||
tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(8,9): error TS2322: Type 'T & U' is not assignable to type 'string | null'.
|
||||
Type 'string | undefined' is not assignable to type 'string | null'.
|
||||
Type 'undefined' is not assignable to type 'string | null'.
|
||||
Type 'T & U' is not assignable to type 'string'.
|
||||
Type 'T & U' is not assignable to type 'string'.
|
||||
tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(10,9): error TS2322: Type 'T & U' is not assignable to type 'number | null'.
|
||||
Type 'string | undefined' is not assignable to type 'number | null'.
|
||||
Type 'undefined' is not assignable to type 'number | null'.
|
||||
Type 'T & U' is not assignable to type 'number'.
|
||||
Type 'T & U' is not assignable to type 'number'.
|
||||
tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(11,9): error TS2322: Type 'T & U' is not assignable to type 'number | undefined'.
|
||||
Type 'string | undefined' is not assignable to type 'number | undefined'.
|
||||
Type 'string' is not assignable to type 'number | undefined'.
|
||||
Type 'T & U' is not assignable to type 'number'.
|
||||
Type 'T & U' is not assignable to type 'number'.
|
||||
tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(12,9): error TS2322: Type 'T & U' is not assignable to type 'null | undefined'.
|
||||
Type 'string | undefined' is not assignable to type 'null | undefined'.
|
||||
Type 'string' is not assignable to type 'null | undefined'.
|
||||
Type 'T & U' is not assignable to type 'null'.
|
||||
Type 'T & U' is not assignable to type 'null'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts (5 errors) ====
|
||||
@ -30,34 +20,24 @@ tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(12
|
||||
let y1: string | number = x; // Error
|
||||
~~
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'string | number'.
|
||||
!!! error TS2322: Type 'string | undefined' is not assignable to type 'string | number'.
|
||||
!!! error TS2322: Type 'undefined' is not assignable to type 'string | number'.
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'number'.
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'number'.
|
||||
let y2: string | null = x; // Error
|
||||
~~
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'string | null'.
|
||||
!!! error TS2322: Type 'string | undefined' is not assignable to type 'string | null'.
|
||||
!!! error TS2322: Type 'undefined' is not assignable to type 'string | null'.
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'string'.
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'string'.
|
||||
let y3: string | undefined = x;
|
||||
let y4: number | null = x; // Error
|
||||
~~
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'number | null'.
|
||||
!!! error TS2322: Type 'string | undefined' is not assignable to type 'number | null'.
|
||||
!!! error TS2322: Type 'undefined' is not assignable to type 'number | null'.
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'number'.
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'number'.
|
||||
let y5: number | undefined = x; // Error
|
||||
~~
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'number | undefined'.
|
||||
!!! error TS2322: Type 'string | undefined' is not assignable to type 'number | undefined'.
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'number | undefined'.
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'number'.
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'number'.
|
||||
let y6: null | undefined = x; // Error
|
||||
~~
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'null | undefined'.
|
||||
!!! error TS2322: Type 'string | undefined' is not assignable to type 'null | undefined'.
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'null | undefined'.
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'null'.
|
||||
!!! error TS2322: Type 'T & U' is not assignable to type 'null'.
|
||||
}
|
||||
|
||||
type T1 = (string | number | undefined) & (string | null | undefined); // string | undefined
|
||||
|
||||
@ -48,8 +48,6 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(103,9): error
|
||||
'string & keyof T' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
|
||||
Type 'string' is not assignable to type 'K'.
|
||||
'string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
|
||||
Type 'string' is not assignable to type 'K'.
|
||||
'string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
|
||||
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(105,9): error TS2322: Type 'T[Extract<keyof T, string>]' is not assignable to type 'T[K]'.
|
||||
Type 'Extract<keyof T, string>' is not assignable to type 'K'.
|
||||
'Extract<keyof T, string>' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
|
||||
@ -68,8 +66,6 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(114,5): error
|
||||
'string & keyof T' is assignable to the constraint of type 'J', but 'J' could be instantiated with a different subtype of constraint 'string'.
|
||||
Type 'string' is not assignable to type 'J'.
|
||||
'string' is assignable to the constraint of type 'J', but 'J' could be instantiated with a different subtype of constraint 'string'.
|
||||
Type 'string' is not assignable to type 'J'.
|
||||
'string' is assignable to the constraint of type 'J', but 'J' could be instantiated with a different subtype of constraint 'string'.
|
||||
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(117,5): error TS2322: Type 'T[K]' is not assignable to type 'U[J]'.
|
||||
Type 'T' is not assignable to type 'U'.
|
||||
'T' is assignable to the constraint of type 'U', but 'U' could be instantiated with a different subtype of constraint '{}'.
|
||||
@ -264,8 +260,6 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(142,5): error
|
||||
!!! error TS2322: 'string & keyof T' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'K'.
|
||||
!!! error TS2322: 'string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'K'.
|
||||
!!! error TS2322: 'string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
|
||||
t[key] = tk; // ok, T[K] ==> T[keyof T]
|
||||
tk = t[key]; // error, T[keyof T] =/=> T[K]
|
||||
~~
|
||||
@ -299,8 +293,6 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(142,5): error
|
||||
!!! error TS2322: 'string & keyof T' is assignable to the constraint of type 'J', but 'J' could be instantiated with a different subtype of constraint 'string'.
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'J'.
|
||||
!!! error TS2322: 'string' is assignable to the constraint of type 'J', but 'J' could be instantiated with a different subtype of constraint 'string'.
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'J'.
|
||||
!!! error TS2322: 'string' is assignable to the constraint of type 'J', but 'J' could be instantiated with a different subtype of constraint 'string'.
|
||||
|
||||
tk = uj;
|
||||
uj = tk; // error
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
//// [typeParameterExtendsUnionConstraintDistributed.ts]
|
||||
type A = 1 | 2;
|
||||
function f<T extends A>(a: T): A & T { return a; } // Shouldn't error
|
||||
|
||||
type B = 2 | 3;
|
||||
function f2<T extends A, U extends B>(ab: T & U): (A | B) & T & U { return ab; } // Also shouldn't error
|
||||
|
||||
|
||||
//// [typeParameterExtendsUnionConstraintDistributed.js]
|
||||
function f(a) { return a; } // Shouldn't error
|
||||
function f2(ab) { return ab; } // Also shouldn't error
|
||||
@ -0,0 +1,32 @@
|
||||
=== tests/cases/conformance/jsdoc/typeParameterExtendsUnionConstraintDistributed.ts ===
|
||||
type A = 1 | 2;
|
||||
>A : Symbol(A, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 0, 0))
|
||||
|
||||
function f<T extends A>(a: T): A & T { return a; } // Shouldn't error
|
||||
>f : Symbol(f, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 0, 15))
|
||||
>T : Symbol(T, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 1, 11))
|
||||
>A : Symbol(A, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 0, 0))
|
||||
>a : Symbol(a, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 1, 24))
|
||||
>T : Symbol(T, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 1, 11))
|
||||
>A : Symbol(A, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 1, 11))
|
||||
>a : Symbol(a, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 1, 24))
|
||||
|
||||
type B = 2 | 3;
|
||||
>B : Symbol(B, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 1, 50))
|
||||
|
||||
function f2<T extends A, U extends B>(ab: T & U): (A | B) & T & U { return ab; } // Also shouldn't error
|
||||
>f2 : Symbol(f2, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 3, 15))
|
||||
>T : Symbol(T, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 4, 12))
|
||||
>A : Symbol(A, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 0, 0))
|
||||
>U : Symbol(U, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 4, 24))
|
||||
>B : Symbol(B, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 1, 50))
|
||||
>ab : Symbol(ab, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 4, 38))
|
||||
>T : Symbol(T, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 4, 12))
|
||||
>U : Symbol(U, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 4, 24))
|
||||
>A : Symbol(A, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 0, 0))
|
||||
>B : Symbol(B, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 1, 50))
|
||||
>T : Symbol(T, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 4, 12))
|
||||
>U : Symbol(U, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 4, 24))
|
||||
>ab : Symbol(ab, Decl(typeParameterExtendsUnionConstraintDistributed.ts, 4, 38))
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
=== tests/cases/conformance/jsdoc/typeParameterExtendsUnionConstraintDistributed.ts ===
|
||||
type A = 1 | 2;
|
||||
>A : A
|
||||
|
||||
function f<T extends A>(a: T): A & T { return a; } // Shouldn't error
|
||||
>f : <T extends A>(a: T) => (1 & T) | (2 & T)
|
||||
>a : T
|
||||
>a : T
|
||||
|
||||
type B = 2 | 3;
|
||||
>B : B
|
||||
|
||||
function f2<T extends A, U extends B>(ab: T & U): (A | B) & T & U { return ab; } // Also shouldn't error
|
||||
>f2 : <T extends A, U extends B>(ab: T & U) => (1 & T & U) | (2 & T & U) | (3 & T & U)
|
||||
>ab : T & U
|
||||
>ab : T & U
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
type A = 1 | 2;
|
||||
function f<T extends A>(a: T): A & T { return a; } // Shouldn't error
|
||||
|
||||
type B = 2 | 3;
|
||||
function f2<T extends A, U extends B>(ab: T & U): (A | B) & T & U { return ab; } // Also shouldn't error
|
||||
Loading…
x
Reference in New Issue
Block a user