Merge pull request #23672 from Microsoft/intersectionWithUnionConstraint
Type relationships for intersections with union constraints
This commit is contained in:
commit
6c28da328e
16 changed files with 506 additions and 28 deletions
|
@ -6470,6 +6470,47 @@ namespace ts {
|
|||
return getConstraintOfDistributiveConditionalType(type) || getDefaultConstraintOfConditionalType(type);
|
||||
}
|
||||
|
||||
function getUnionConstraintOfIntersection(type: IntersectionType, targetIsUnion: boolean) {
|
||||
let constraints: Type[];
|
||||
let hasDisjointDomainType = false;
|
||||
for (const t of type.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).
|
||||
let constraint = getConstraintOfType(t);
|
||||
while (constraint && constraint.flags & (TypeFlags.TypeParameter | TypeFlags.Index | TypeFlags.Conditional)) {
|
||||
constraint = getConstraintOfType(constraint);
|
||||
}
|
||||
if (constraint) {
|
||||
// A constraint that isn't a union type implies that the final type would be a non-union
|
||||
// type as well. Since non-union constraints are of no interest, we can exit here.
|
||||
if (!(constraint.flags & TypeFlags.Union)) {
|
||||
return undefined;
|
||||
}
|
||||
constraints = append(constraints, constraint);
|
||||
}
|
||||
}
|
||||
else if (t.flags & TypeFlags.DisjointDomains) {
|
||||
hasDisjointDomainType = true;
|
||||
}
|
||||
}
|
||||
// If the target is a union type or if we are intersecting with types belonging to one of the
|
||||
// disjoint domans, we may end up producing a constraint that hasn't been examined before.
|
||||
if (constraints && (targetIsUnion || hasDisjointDomainType)) {
|
||||
if (hasDisjointDomainType) {
|
||||
// We add any types belong to one of the disjoint domans because they might cause the final
|
||||
// intersection operation to reduce the union constraints.
|
||||
for (const t of type.types) {
|
||||
if (t.flags & TypeFlags.DisjointDomains) {
|
||||
constraints = append(constraints, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
return getIntersectionType(constraints);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getBaseConstraintOfInstantiableNonPrimitiveUnionOrIntersection(type: Type) {
|
||||
if (type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.UnionOrIntersection)) {
|
||||
const constraint = getResolvedBaseConstraint(<InstantiableType | UnionOrIntersectionType>type);
|
||||
|
@ -7951,8 +7992,14 @@ namespace ts {
|
|||
return binarySearch(types, type, getTypeId, compareValues) >= 0;
|
||||
}
|
||||
|
||||
// Return true if the given intersection type contains (a) more than one unit type or (b) an object
|
||||
// type and a nullable type (null or undefined).
|
||||
// Return true if the given intersection type contains
|
||||
// more than one unit type or,
|
||||
// an object type and a nullable type (null or undefined), or
|
||||
// a string-like type and a type known to be non-string-like, or
|
||||
// a number-like type and a type known to be non-number-like, or
|
||||
// a symbol-like type and a type known to be non-symbol-like, or
|
||||
// a void-like type and a type known to be non-void-like, or
|
||||
// a non-primitive type and a type known to be primitive.
|
||||
function isEmptyIntersectionType(type: IntersectionType) {
|
||||
let combined: TypeFlags = 0;
|
||||
for (const t of type.types) {
|
||||
|
@ -7960,7 +8007,12 @@ namespace ts {
|
|||
return true;
|
||||
}
|
||||
combined |= t.flags;
|
||||
if (combined & TypeFlags.Nullable && combined & (TypeFlags.Object | TypeFlags.NonPrimitive)) {
|
||||
if (combined & TypeFlags.Nullable && combined & (TypeFlags.Object | TypeFlags.NonPrimitive) ||
|
||||
combined & TypeFlags.NonPrimitive && combined & (TypeFlags.DisjointDomains & ~TypeFlags.NonPrimitive) ||
|
||||
combined & TypeFlags.StringLike && combined & (TypeFlags.DisjointDomains & ~TypeFlags.StringLike) ||
|
||||
combined & TypeFlags.NumberLike && combined & (TypeFlags.DisjointDomains & ~TypeFlags.NumberLike) ||
|
||||
combined & TypeFlags.ESSymbolLike && combined & (TypeFlags.DisjointDomains & ~TypeFlags.ESSymbolLike) ||
|
||||
combined & TypeFlags.VoidLike && combined & (TypeFlags.DisjointDomains & ~TypeFlags.VoidLike)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -10180,6 +10232,23 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!result && source.flags & TypeFlags.Intersection) {
|
||||
// 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
|
||||
// when the target is a union type. Another is when the intersection contains types belonging
|
||||
// to one of the disjoint domains. For example, given type variables T and U, each with the
|
||||
// constraint 'string | number', the combined constraint of 'T & U' is 'string | number' and
|
||||
// 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)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isIntersectionConstituent = saveIsIntersectionConstituent;
|
||||
|
||||
|
|
|
@ -3620,6 +3620,9 @@ namespace ts {
|
|||
BooleanLike = Boolean | BooleanLiteral,
|
||||
EnumLike = Enum | EnumLiteral,
|
||||
ESSymbolLike = ESSymbol | UniqueESSymbol,
|
||||
VoidLike = Void | Undefined,
|
||||
/* @internal */
|
||||
DisjointDomains = NonPrimitive | StringLike | NumberLike | BooleanLike | ESSymbolLike | VoidLike | Null,
|
||||
UnionOrIntersection = Union | Intersection,
|
||||
StructuredType = Object | Union | Intersection,
|
||||
TypeVariable = TypeParameter | IndexedAccess,
|
||||
|
|
|
@ -2111,6 +2111,7 @@ declare namespace ts {
|
|||
BooleanLike = 136,
|
||||
EnumLike = 272,
|
||||
ESSymbolLike = 1536,
|
||||
VoidLike = 6144,
|
||||
UnionOrIntersection = 393216,
|
||||
StructuredType = 458752,
|
||||
TypeVariable = 1081344,
|
||||
|
|
|
@ -2111,6 +2111,7 @@ declare namespace ts {
|
|||
BooleanLike = 136,
|
||||
EnumLike = 272,
|
||||
ESSymbolLike = 1536,
|
||||
VoidLike = 6144,
|
||||
UnionOrIntersection = 393216,
|
||||
StructuredType = 458752,
|
||||
TypeVariable = 1081344,
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
tests/cases/compiler/errorMessagesIntersectionTypes04.ts(17,5): error TS2322: Type 'A & B' is not assignable to type 'number'.
|
||||
tests/cases/compiler/errorMessagesIntersectionTypes04.ts(18,5): error TS2322: Type 'A & B' is not assignable to type 'boolean'.
|
||||
tests/cases/compiler/errorMessagesIntersectionTypes04.ts(19,5): error TS2322: Type 'A & B' is not assignable to type 'string'.
|
||||
tests/cases/compiler/errorMessagesIntersectionTypes04.ts(21,5): error TS2322: Type '(number & true) | (number & false)' is not assignable to type 'string'.
|
||||
Type 'number & true' is not assignable to type 'string'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/errorMessagesIntersectionTypes04.ts (4 errors) ====
|
||||
==== tests/cases/compiler/errorMessagesIntersectionTypes04.ts (3 errors) ====
|
||||
interface A {
|
||||
a;
|
||||
}
|
||||
|
@ -33,7 +31,4 @@ tests/cases/compiler/errorMessagesIntersectionTypes04.ts(21,5): error TS2322: Ty
|
|||
!!! error TS2322: Type 'A & B' is not assignable to type 'string'.
|
||||
|
||||
str = num_and_bool;
|
||||
~~~
|
||||
!!! error TS2322: Type '(number & true) | (number & false)' is not assignable to type 'string'.
|
||||
!!! error TS2322: Type 'number & true' is not assignable to type 'string'.
|
||||
}
|
|
@ -36,7 +36,7 @@ function f<T, U extends A, V extends U>(): void {
|
|||
>B : B
|
||||
|
||||
let num_and_bool: number & boolean;
|
||||
>num_and_bool : (number & true) | (number & false)
|
||||
>num_and_bool : never
|
||||
|
||||
num = a_and_b;
|
||||
>num = a_and_b : A & B
|
||||
|
@ -54,7 +54,7 @@ function f<T, U extends A, V extends U>(): void {
|
|||
>a_and_b : A & B
|
||||
|
||||
str = num_and_bool;
|
||||
>str = num_and_bool : (number & true) | (number & false)
|
||||
>str = num_and_bool : never
|
||||
>str : string
|
||||
>num_and_bool : (number & true) | (number & false)
|
||||
>num_and_bool : never
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
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'.
|
||||
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'.
|
||||
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'.
|
||||
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'.
|
||||
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'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts (5 errors) ====
|
||||
function f1<T extends string | number, U extends string | number>(x: T & U) {
|
||||
// Combined constraint of 'T & U' is 'string | number'
|
||||
let y: string | number = x;
|
||||
}
|
||||
|
||||
function f2<T extends string | number | undefined, U extends string | null | undefined>(x: T & U) {
|
||||
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'.
|
||||
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'.
|
||||
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'.
|
||||
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'.
|
||||
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'.
|
||||
}
|
||||
|
||||
type T1 = (string | number | undefined) & (string | null | undefined); // string | undefined
|
||||
|
||||
function f3<T extends string | number | undefined>(x: T & (number | object | undefined)) {
|
||||
const y: number | undefined = x;
|
||||
}
|
||||
|
||||
function f4<T extends string | number>(x: T & (number | object)) {
|
||||
const y: number = x;
|
||||
}
|
||||
|
||||
function f5<T, U extends keyof T>(x: keyof T & U) {
|
||||
let y: keyof any = x;
|
||||
}
|
||||
|
||||
// Repro from #23648
|
||||
|
||||
type Example<T, U> = { [K in keyof T]: K extends keyof U ? UnexpectedError<K> : NoErrorHere<K> }
|
||||
|
||||
type UnexpectedError<T extends PropertyKey> = T
|
||||
type NoErrorHere<T extends PropertyKey> = T
|
||||
|
60
tests/baselines/reference/intersectionWithUnionConstraint.js
Normal file
60
tests/baselines/reference/intersectionWithUnionConstraint.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
//// [intersectionWithUnionConstraint.ts]
|
||||
function f1<T extends string | number, U extends string | number>(x: T & U) {
|
||||
// Combined constraint of 'T & U' is 'string | number'
|
||||
let y: string | number = x;
|
||||
}
|
||||
|
||||
function f2<T extends string | number | undefined, U extends string | null | undefined>(x: T & U) {
|
||||
let y1: string | number = x; // Error
|
||||
let y2: string | null = x; // Error
|
||||
let y3: string | undefined = x;
|
||||
let y4: number | null = x; // Error
|
||||
let y5: number | undefined = x; // Error
|
||||
let y6: null | undefined = x; // Error
|
||||
}
|
||||
|
||||
type T1 = (string | number | undefined) & (string | null | undefined); // string | undefined
|
||||
|
||||
function f3<T extends string | number | undefined>(x: T & (number | object | undefined)) {
|
||||
const y: number | undefined = x;
|
||||
}
|
||||
|
||||
function f4<T extends string | number>(x: T & (number | object)) {
|
||||
const y: number = x;
|
||||
}
|
||||
|
||||
function f5<T, U extends keyof T>(x: keyof T & U) {
|
||||
let y: keyof any = x;
|
||||
}
|
||||
|
||||
// Repro from #23648
|
||||
|
||||
type Example<T, U> = { [K in keyof T]: K extends keyof U ? UnexpectedError<K> : NoErrorHere<K> }
|
||||
|
||||
type UnexpectedError<T extends PropertyKey> = T
|
||||
type NoErrorHere<T extends PropertyKey> = T
|
||||
|
||||
|
||||
//// [intersectionWithUnionConstraint.js]
|
||||
"use strict";
|
||||
function f1(x) {
|
||||
// Combined constraint of 'T & U' is 'string | number'
|
||||
var y = x;
|
||||
}
|
||||
function f2(x) {
|
||||
var y1 = x; // Error
|
||||
var y2 = x; // Error
|
||||
var y3 = x;
|
||||
var y4 = x; // Error
|
||||
var y5 = x; // Error
|
||||
var y6 = x; // Error
|
||||
}
|
||||
function f3(x) {
|
||||
var y = x;
|
||||
}
|
||||
function f4(x) {
|
||||
var y = x;
|
||||
}
|
||||
function f5(x) {
|
||||
var y = x;
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
=== tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts ===
|
||||
function f1<T extends string | number, U extends string | number>(x: T & U) {
|
||||
>f1 : Symbol(f1, Decl(intersectionWithUnionConstraint.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 0, 12))
|
||||
>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 0, 38))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 0, 66))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 0, 12))
|
||||
>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 0, 38))
|
||||
|
||||
// Combined constraint of 'T & U' is 'string | number'
|
||||
let y: string | number = x;
|
||||
>y : Symbol(y, Decl(intersectionWithUnionConstraint.ts, 2, 7))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 0, 66))
|
||||
}
|
||||
|
||||
function f2<T extends string | number | undefined, U extends string | null | undefined>(x: T & U) {
|
||||
>f2 : Symbol(f2, Decl(intersectionWithUnionConstraint.ts, 3, 1))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 5, 12))
|
||||
>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 5, 50))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 5, 88))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 5, 12))
|
||||
>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 5, 50))
|
||||
|
||||
let y1: string | number = x; // Error
|
||||
>y1 : Symbol(y1, Decl(intersectionWithUnionConstraint.ts, 6, 7))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 5, 88))
|
||||
|
||||
let y2: string | null = x; // Error
|
||||
>y2 : Symbol(y2, Decl(intersectionWithUnionConstraint.ts, 7, 7))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 5, 88))
|
||||
|
||||
let y3: string | undefined = x;
|
||||
>y3 : Symbol(y3, Decl(intersectionWithUnionConstraint.ts, 8, 7))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 5, 88))
|
||||
|
||||
let y4: number | null = x; // Error
|
||||
>y4 : Symbol(y4, Decl(intersectionWithUnionConstraint.ts, 9, 7))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 5, 88))
|
||||
|
||||
let y5: number | undefined = x; // Error
|
||||
>y5 : Symbol(y5, Decl(intersectionWithUnionConstraint.ts, 10, 7))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 5, 88))
|
||||
|
||||
let y6: null | undefined = x; // Error
|
||||
>y6 : Symbol(y6, Decl(intersectionWithUnionConstraint.ts, 11, 7))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 5, 88))
|
||||
}
|
||||
|
||||
type T1 = (string | number | undefined) & (string | null | undefined); // string | undefined
|
||||
>T1 : Symbol(T1, Decl(intersectionWithUnionConstraint.ts, 12, 1))
|
||||
|
||||
function f3<T extends string | number | undefined>(x: T & (number | object | undefined)) {
|
||||
>f3 : Symbol(f3, Decl(intersectionWithUnionConstraint.ts, 14, 70))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 16, 12))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 16, 51))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 16, 12))
|
||||
|
||||
const y: number | undefined = x;
|
||||
>y : Symbol(y, Decl(intersectionWithUnionConstraint.ts, 17, 9))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 16, 51))
|
||||
}
|
||||
|
||||
function f4<T extends string | number>(x: T & (number | object)) {
|
||||
>f4 : Symbol(f4, Decl(intersectionWithUnionConstraint.ts, 18, 1))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 20, 12))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 20, 39))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 20, 12))
|
||||
|
||||
const y: number = x;
|
||||
>y : Symbol(y, Decl(intersectionWithUnionConstraint.ts, 21, 9))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 20, 39))
|
||||
}
|
||||
|
||||
function f5<T, U extends keyof T>(x: keyof T & U) {
|
||||
>f5 : Symbol(f5, Decl(intersectionWithUnionConstraint.ts, 22, 1))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 24, 12))
|
||||
>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 24, 14))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 24, 12))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 24, 34))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 24, 12))
|
||||
>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 24, 14))
|
||||
|
||||
let y: keyof any = x;
|
||||
>y : Symbol(y, Decl(intersectionWithUnionConstraint.ts, 25, 7))
|
||||
>x : Symbol(x, Decl(intersectionWithUnionConstraint.ts, 24, 34))
|
||||
}
|
||||
|
||||
// Repro from #23648
|
||||
|
||||
type Example<T, U> = { [K in keyof T]: K extends keyof U ? UnexpectedError<K> : NoErrorHere<K> }
|
||||
>Example : Symbol(Example, Decl(intersectionWithUnionConstraint.ts, 26, 1))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 30, 13))
|
||||
>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 30, 15))
|
||||
>K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 30, 24))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 30, 13))
|
||||
>K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 30, 24))
|
||||
>U : Symbol(U, Decl(intersectionWithUnionConstraint.ts, 30, 15))
|
||||
>UnexpectedError : Symbol(UnexpectedError, Decl(intersectionWithUnionConstraint.ts, 30, 96))
|
||||
>K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 30, 24))
|
||||
>NoErrorHere : Symbol(NoErrorHere, Decl(intersectionWithUnionConstraint.ts, 32, 47))
|
||||
>K : Symbol(K, Decl(intersectionWithUnionConstraint.ts, 30, 24))
|
||||
|
||||
type UnexpectedError<T extends PropertyKey> = T
|
||||
>UnexpectedError : Symbol(UnexpectedError, Decl(intersectionWithUnionConstraint.ts, 30, 96))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 32, 21))
|
||||
>PropertyKey : Symbol(PropertyKey, Decl(lib.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 32, 21))
|
||||
|
||||
type NoErrorHere<T extends PropertyKey> = T
|
||||
>NoErrorHere : Symbol(NoErrorHere, Decl(intersectionWithUnionConstraint.ts, 32, 47))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 33, 17))
|
||||
>PropertyKey : Symbol(PropertyKey, Decl(lib.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(intersectionWithUnionConstraint.ts, 33, 17))
|
||||
|
119
tests/baselines/reference/intersectionWithUnionConstraint.types
Normal file
119
tests/baselines/reference/intersectionWithUnionConstraint.types
Normal file
|
@ -0,0 +1,119 @@
|
|||
=== tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts ===
|
||||
function f1<T extends string | number, U extends string | number>(x: T & U) {
|
||||
>f1 : <T extends string | number, U extends string | number>(x: T & U) => void
|
||||
>T : T
|
||||
>U : U
|
||||
>x : T & U
|
||||
>T : T
|
||||
>U : U
|
||||
|
||||
// Combined constraint of 'T & U' is 'string | number'
|
||||
let y: string | number = x;
|
||||
>y : string | number
|
||||
>x : T & U
|
||||
}
|
||||
|
||||
function f2<T extends string | number | undefined, U extends string | null | undefined>(x: T & U) {
|
||||
>f2 : <T extends string | number | undefined, U extends string | null | undefined>(x: T & U) => void
|
||||
>T : T
|
||||
>U : U
|
||||
>null : null
|
||||
>x : T & U
|
||||
>T : T
|
||||
>U : U
|
||||
|
||||
let y1: string | number = x; // Error
|
||||
>y1 : string | number
|
||||
>x : T & U
|
||||
|
||||
let y2: string | null = x; // Error
|
||||
>y2 : string | null
|
||||
>null : null
|
||||
>x : T & U
|
||||
|
||||
let y3: string | undefined = x;
|
||||
>y3 : string | undefined
|
||||
>x : T & U
|
||||
|
||||
let y4: number | null = x; // Error
|
||||
>y4 : number | null
|
||||
>null : null
|
||||
>x : T & U
|
||||
|
||||
let y5: number | undefined = x; // Error
|
||||
>y5 : number | undefined
|
||||
>x : T & U
|
||||
|
||||
let y6: null | undefined = x; // Error
|
||||
>y6 : null | undefined
|
||||
>null : null
|
||||
>x : T & U
|
||||
}
|
||||
|
||||
type T1 = (string | number | undefined) & (string | null | undefined); // string | undefined
|
||||
>T1 : string | undefined
|
||||
>null : null
|
||||
|
||||
function f3<T extends string | number | undefined>(x: T & (number | object | undefined)) {
|
||||
>f3 : <T extends string | number | undefined>(x: (T & undefined) | (T & number) | (T & object)) => void
|
||||
>T : T
|
||||
>x : (T & undefined) | (T & number) | (T & object)
|
||||
>T : T
|
||||
|
||||
const y: number | undefined = x;
|
||||
>y : number | undefined
|
||||
>x : (T & undefined) | (T & number) | (T & object)
|
||||
}
|
||||
|
||||
function f4<T extends string | number>(x: T & (number | object)) {
|
||||
>f4 : <T extends string | number>(x: (T & number) | (T & object)) => void
|
||||
>T : T
|
||||
>x : (T & number) | (T & object)
|
||||
>T : T
|
||||
|
||||
const y: number = x;
|
||||
>y : number
|
||||
>x : (T & number) | (T & object)
|
||||
}
|
||||
|
||||
function f5<T, U extends keyof T>(x: keyof T & U) {
|
||||
>f5 : <T, U extends keyof T>(x: keyof T & U) => void
|
||||
>T : T
|
||||
>U : U
|
||||
>T : T
|
||||
>x : keyof T & U
|
||||
>T : T
|
||||
>U : U
|
||||
|
||||
let y: keyof any = x;
|
||||
>y : string | number | symbol
|
||||
>x : keyof T & U
|
||||
}
|
||||
|
||||
// Repro from #23648
|
||||
|
||||
type Example<T, U> = { [K in keyof T]: K extends keyof U ? UnexpectedError<K> : NoErrorHere<K> }
|
||||
>Example : Example<T, U>
|
||||
>T : T
|
||||
>U : U
|
||||
>K : K
|
||||
>T : T
|
||||
>K : K
|
||||
>U : U
|
||||
>UnexpectedError : T
|
||||
>K : K
|
||||
>NoErrorHere : T
|
||||
>K : K
|
||||
|
||||
type UnexpectedError<T extends PropertyKey> = T
|
||||
>UnexpectedError : T
|
||||
>T : T
|
||||
>PropertyKey : string | number | symbol
|
||||
>T : T
|
||||
|
||||
type NoErrorHere<T extends PropertyKey> = T
|
||||
>NoErrorHere : T
|
||||
>T : T
|
||||
>PropertyKey : string | number | symbol
|
||||
>T : T
|
||||
|
|
@ -44,6 +44,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(87,5): error
|
|||
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(103,9): error TS2322: Type 'Extract<keyof T, string>' is not assignable to type 'K'.
|
||||
Type 'string & keyof T' is not assignable to type 'K'.
|
||||
Type 'string' is not assignable to type 'K'.
|
||||
Type 'string' is not assignable to type 'K'.
|
||||
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'.
|
||||
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(108,5): error TS2322: Type 'T[K]' is not assignable to type 'U[K]'.
|
||||
|
@ -55,6 +56,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(114,5): error
|
|||
Type 'Extract<keyof T, string>' is not assignable to type 'J'.
|
||||
Type 'string & keyof T' is not assignable to type 'J'.
|
||||
Type 'string' is not assignable to type 'J'.
|
||||
Type 'string' is not assignable to type 'J'.
|
||||
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'.
|
||||
|
||||
|
@ -238,6 +240,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(117,5): error
|
|||
!!! error TS2322: Type 'Extract<keyof T, string>' is not assignable to type 'K'.
|
||||
!!! error TS2322: Type 'string & keyof T' is not assignable to type 'K'.
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'K'.
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'K'.
|
||||
t[key] = tk; // ok, T[K] ==> T[keyof T]
|
||||
tk = t[key]; // error, T[keyof T] =/=> T[K]
|
||||
~~
|
||||
|
@ -264,6 +267,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(117,5): error
|
|||
!!! error TS2322: Type 'Extract<keyof T, string>' is not assignable to type 'J'.
|
||||
!!! error TS2322: Type 'string & keyof T' is not assignable to type 'J'.
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'J'.
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'J'.
|
||||
|
||||
tk = uj;
|
||||
uj = tk; // error
|
||||
|
|
|
@ -34,19 +34,19 @@ type T02 = keyof keyof Object;
|
|||
>Object : Object
|
||||
|
||||
type T03 = keyof keyof keyof Object;
|
||||
>T03 : "toString" | "valueOf" | ("toString" & number) | ("toLocaleString" & number) | ("valueOf" & number) | ("toFixed" & number) | ("toExponential" & number) | ("toPrecision" & number)
|
||||
>T03 : "toString" | "valueOf"
|
||||
>Object : Object
|
||||
|
||||
type T04 = keyof keyof keyof keyof Object;
|
||||
>T04 : number | "length" | "toString" | "valueOf" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | ("toString" & number) | ("valueOf" & number) | (number & "length") | (number & "toString") | (number & "toLocaleString") | (number & "valueOf") | (number & "charAt") | (number & "charCodeAt") | (number & "concat") | (number & "indexOf") | (number & "lastIndexOf") | (number & "localeCompare") | (number & "match") | (number & "replace") | (number & "search") | (number & "slice") | (number & "split") | (number & "substring") | (number & "toLowerCase") | (number & "toLocaleLowerCase") | (number & "toUpperCase") | (number & "toLocaleUpperCase") | (number & "trim") | (number & "substr") | (number & "toFixed") | (number & "toExponential") | (number & "toPrecision") | ("length" & number) | ("charAt" & number) | ("charCodeAt" & number) | ("concat" & number) | ("indexOf" & number) | ("lastIndexOf" & number) | ("localeCompare" & number) | ("match" & number) | ("replace" & number) | ("search" & number) | ("slice" & number) | ("split" & number) | ("substring" & number) | ("toLowerCase" & number) | ("toLocaleLowerCase" & number) | ("toUpperCase" & number) | ("toLocaleUpperCase" & number) | ("trim" & number) | ("substr" & number)
|
||||
>T04 : number | "length" | "toString" | "valueOf" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr"
|
||||
>Object : Object
|
||||
|
||||
type T05 = keyof keyof keyof keyof keyof Object;
|
||||
>T05 : "toString" | "valueOf" | ("toString" & number) | ("toLocaleString" & number) | ("valueOf" & number) | ("toFixed" & number) | ("toExponential" & number) | ("toPrecision" & number)
|
||||
>T05 : "toString" | "valueOf"
|
||||
>Object : Object
|
||||
|
||||
type T06 = keyof keyof keyof keyof keyof keyof Object;
|
||||
>T06 : number | "length" | "toString" | "valueOf" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | ("toString" & number) | ("valueOf" & number) | (number & "length") | (number & "toString") | (number & "toLocaleString") | (number & "valueOf") | (number & "charAt") | (number & "charCodeAt") | (number & "concat") | (number & "indexOf") | (number & "lastIndexOf") | (number & "localeCompare") | (number & "match") | (number & "replace") | (number & "search") | (number & "slice") | (number & "split") | (number & "substring") | (number & "toLowerCase") | (number & "toLocaleLowerCase") | (number & "toUpperCase") | (number & "toLocaleUpperCase") | (number & "trim") | (number & "substr") | (number & "toFixed") | (number & "toExponential") | (number & "toPrecision") | ("length" & number) | ("charAt" & number) | ("charCodeAt" & number) | ("concat" & number) | ("indexOf" & number) | ("lastIndexOf" & number) | ("localeCompare" & number) | ("match" & number) | ("replace" & number) | ("search" & number) | ("slice" & number) | ("split" & number) | ("substring" & number) | ("toLowerCase" & number) | ("toLocaleLowerCase" & number) | ("toUpperCase" & number) | ("toLocaleUpperCase" & number) | ("trim" & number) | ("substr" & number)
|
||||
>T06 : number | "length" | "toString" | "valueOf" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr"
|
||||
>Object : Object
|
||||
|
||||
type T10 = Shape["name"];
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
tests/cases/conformance/types/typeRelationships/comparable/switchCaseWithIntersectionTypes01.ts(18,10): error TS2678: Type '(number & true) | (number & false)' is not comparable to type 'string & number'.
|
||||
Type 'number & false' is not comparable to type 'string & number'.
|
||||
Type 'number & false' is not comparable to type 'string'.
|
||||
tests/cases/conformance/types/typeRelationships/comparable/switchCaseWithIntersectionTypes01.ts(22,10): error TS2678: Type 'boolean' is not comparable to type 'string & number'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/types/typeRelationships/comparable/switchCaseWithIntersectionTypes01.ts (2 errors) ====
|
||||
==== tests/cases/conformance/types/typeRelationships/comparable/switchCaseWithIntersectionTypes01.ts (1 errors) ====
|
||||
var strAndNum: string & number;
|
||||
var numAndBool: number & boolean;
|
||||
var str: string;
|
||||
|
@ -23,10 +20,6 @@ tests/cases/conformance/types/typeRelationships/comparable/switchCaseWithInterse
|
|||
|
||||
// Overlap in constituents
|
||||
case numAndBool:
|
||||
~~~~~~~~~~
|
||||
!!! error TS2678: Type '(number & true) | (number & false)' is not comparable to type 'string & number'.
|
||||
!!! error TS2678: Type 'number & false' is not comparable to type 'string & number'.
|
||||
!!! error TS2678: Type 'number & false' is not comparable to type 'string'.
|
||||
break;
|
||||
|
||||
// No relation
|
||||
|
|
|
@ -3,7 +3,7 @@ var strAndNum: string & number;
|
|||
>strAndNum : string & number
|
||||
|
||||
var numAndBool: number & boolean;
|
||||
>numAndBool : (number & true) | (number & false)
|
||||
>numAndBool : never
|
||||
|
||||
var str: string;
|
||||
>str : string
|
||||
|
@ -34,7 +34,7 @@ switch (strAndNum) {
|
|||
|
||||
// Overlap in constituents
|
||||
case numAndBool:
|
||||
>numAndBool : (number & true) | (number & false)
|
||||
>numAndBool : never
|
||||
|
||||
break;
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ if (!(result instanceof RegExp)) {
|
|||
|
||||
} else if (!result.global) {
|
||||
>!result.global : boolean
|
||||
>result.global : (string & true) | (string & false)
|
||||
>result.global : never
|
||||
>result : I & RegExp
|
||||
>global : (string & true) | (string & false)
|
||||
>global : never
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
// @strict: true
|
||||
|
||||
function f1<T extends string | number, U extends string | number>(x: T & U) {
|
||||
// Combined constraint of 'T & U' is 'string | number'
|
||||
let y: string | number = x;
|
||||
}
|
||||
|
||||
function f2<T extends string | number | undefined, U extends string | null | undefined>(x: T & U) {
|
||||
let y1: string | number = x; // Error
|
||||
let y2: string | null = x; // Error
|
||||
let y3: string | undefined = x;
|
||||
let y4: number | null = x; // Error
|
||||
let y5: number | undefined = x; // Error
|
||||
let y6: null | undefined = x; // Error
|
||||
}
|
||||
|
||||
type T1 = (string | number | undefined) & (string | null | undefined); // string | undefined
|
||||
|
||||
function f3<T extends string | number | undefined>(x: T & (number | object | undefined)) {
|
||||
const y: number | undefined = x;
|
||||
}
|
||||
|
||||
function f4<T extends string | number>(x: T & (number | object)) {
|
||||
const y: number = x;
|
||||
}
|
||||
|
||||
function f5<T, U extends keyof T>(x: keyof T & U) {
|
||||
let y: keyof any = x;
|
||||
}
|
||||
|
||||
// Repro from #23648
|
||||
|
||||
type Example<T, U> = { [K in keyof T]: K extends keyof U ? UnexpectedError<K> : NoErrorHere<K> }
|
||||
|
||||
type UnexpectedError<T extends PropertyKey> = T
|
||||
type NoErrorHere<T extends PropertyKey> = T
|
Loading…
Reference in a new issue