Skip to content

Commit b100680

Browse files
authored
Fix for relating covered discriminants in unions (#39393)
2 parents 0c75021 + 8eba362 commit b100680

File tree

6 files changed

+104
-4
lines changed

6 files changed

+104
-4
lines changed

src/compiler/checker.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -17279,7 +17279,11 @@ namespace ts {
1727917279
result &= signaturesRelatedTo(source, type, SignatureKind.Construct, /*reportStructuralErrors*/ false);
1728017280
if (result) {
1728117281
result &= indexTypesRelatedTo(source, type, IndexKind.String, /*sourceIsPrimitive*/ false, /*reportStructuralErrors*/ false, IntersectionState.None);
17282-
if (result) {
17282+
// Comparing numeric index types when both `source` and `type` are tuples is unnecessary as the
17283+
// element types should be sufficiently covered by `propertiesRelatedTo`. It also causes problems
17284+
// with index type assignability as the types for the excluded discriminants are still included
17285+
// in the index type.
17286+
if (result && !(isTupleType(source) && isTupleType(type))) {
1728317287
result &= indexTypesRelatedTo(source, type, IndexKind.Number, /*sourceIsPrimitive*/ false, /*reportStructuralErrors*/ false, IntersectionState.None);
1728417288
}
1728517289
}
@@ -17504,6 +17508,7 @@ namespace ts {
1750417508
for (let i = 0; i < maxArity; i++) {
1750517509
const targetFlags = i < targetArity ? target.target.elementFlags[i] : targetRestFlag;
1750617510
const sourceFlags = isTupleType(source) && i < sourceArity ? source.target.elementFlags[i] : sourceRestFlag;
17511+
let canExcludeDiscriminants = !!excludedProperties;
1750717512
if (sourceFlags && targetFlags) {
1750817513
if (targetFlags & ElementFlags.Variadic && !(sourceFlags & ElementFlags.Variadic) ||
1750917514
(sourceFlags & ElementFlags.Variadic && !(targetFlags & ElementFlags.Variable))) {
@@ -17520,6 +17525,15 @@ namespace ts {
1752017525
return Ternary.False;
1752117526
}
1752217527
}
17528+
// We can only exclude discriminant properties if we have not yet encountered a variable-length element.
17529+
if (canExcludeDiscriminants) {
17530+
if (sourceFlags & ElementFlags.Variable || targetFlags & ElementFlags.Variable) {
17531+
canExcludeDiscriminants = false;
17532+
}
17533+
if (canExcludeDiscriminants && excludedProperties?.has(("" + i) as __String)) {
17534+
continue;
17535+
}
17536+
}
1752317537
const sourceType = getTypeArguments(source)[Math.min(i, sourceArity - 1)];
1752417538
const targetType = getTypeArguments(target)[Math.min(i, targetArity - 1)];
1752517539
const targetCheckType = sourceFlags & ElementFlags.Variadic && targetFlags & ElementFlags.Rest ? createArrayType(targetType) : targetType;

tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.errors.txt

+10-1
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,13 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
221221
type: obj1.type
222222
};
223223
}
224-
}
224+
}
225+
226+
// https://github.com/microsoft/TypeScript/issues/39357
227+
namespace GH39357 {
228+
type A = ["a", number] | ["b", number] | ["c", string];
229+
type B = "a" | "b" | "c";
230+
declare const b: B;
231+
const a: A = b === "a" || b === "b" ? [b, 1] : ["c", ""];
232+
}
233+

tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.js

+15-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,16 @@ namespace GH20889 {
191191
type: obj1.type
192192
};
193193
}
194-
}
194+
}
195+
196+
// https://github.com/microsoft/TypeScript/issues/39357
197+
namespace GH39357 {
198+
type A = ["a", number] | ["b", number] | ["c", string];
199+
type B = "a" | "b" | "c";
200+
declare const b: B;
201+
const a: A = b === "a" || b === "b" ? [b, 1] : ["c", ""];
202+
}
203+
195204

196205
//// [assignmentCompatWithDiscriminatedUnion.js]
197206
// see 'typeRelatedToDiscriminatedType' in checker.ts:
@@ -289,3 +298,8 @@ var GH20889;
289298
};
290299
}
291300
})(GH20889 || (GH20889 = {}));
301+
// https://github.com/microsoft/TypeScript/issues/39357
302+
var GH39357;
303+
(function (GH39357) {
304+
var a = b === "a" || b === "b" ? [b, 1] : ["c", ""];
305+
})(GH39357 || (GH39357 = {}));

tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.symbols

+23
Original file line numberDiff line numberDiff line change
@@ -492,3 +492,26 @@ namespace GH20889 {
492492
};
493493
}
494494
}
495+
496+
// https://github.com/microsoft/TypeScript/issues/39357
497+
namespace GH39357 {
498+
>GH39357 : Symbol(GH39357, Decl(assignmentCompatWithDiscriminatedUnion.ts, 192, 1))
499+
500+
type A = ["a", number] | ["b", number] | ["c", string];
501+
>A : Symbol(A, Decl(assignmentCompatWithDiscriminatedUnion.ts, 195, 19))
502+
503+
type B = "a" | "b" | "c";
504+
>B : Symbol(B, Decl(assignmentCompatWithDiscriminatedUnion.ts, 196, 59))
505+
506+
declare const b: B;
507+
>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 198, 17))
508+
>B : Symbol(B, Decl(assignmentCompatWithDiscriminatedUnion.ts, 196, 59))
509+
510+
const a: A = b === "a" || b === "b" ? [b, 1] : ["c", ""];
511+
>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 199, 9))
512+
>A : Symbol(A, Decl(assignmentCompatWithDiscriminatedUnion.ts, 195, 19))
513+
>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 198, 17))
514+
>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 198, 17))
515+
>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 198, 17))
516+
}
517+

tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.types

+32
Original file line numberDiff line numberDiff line change
@@ -464,3 +464,35 @@ namespace GH20889 {
464464
};
465465
}
466466
}
467+
468+
// https://github.com/microsoft/TypeScript/issues/39357
469+
namespace GH39357 {
470+
>GH39357 : typeof GH39357
471+
472+
type A = ["a", number] | ["b", number] | ["c", string];
473+
>A : A
474+
475+
type B = "a" | "b" | "c";
476+
>B : "a" | "b" | "c"
477+
478+
declare const b: B;
479+
>b : "a" | "b" | "c"
480+
481+
const a: A = b === "a" || b === "b" ? [b, 1] : ["c", ""];
482+
>a : A
483+
>b === "a" || b === "b" ? [b, 1] : ["c", ""] : ["a" | "b", number] | ["c", string]
484+
>b === "a" || b === "b" : boolean
485+
>b === "a" : boolean
486+
>b : "a" | "b" | "c"
487+
>"a" : "a"
488+
>b === "b" : boolean
489+
>b : "b" | "c"
490+
>"b" : "b"
491+
>[b, 1] : ["a" | "b", number]
492+
>b : "a" | "b"
493+
>1 : 1
494+
>["c", ""] : ["c", string]
495+
>"c" : "c"
496+
>"" : ""
497+
}
498+

tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,12 @@ namespace GH20889 {
190190
type: obj1.type
191191
};
192192
}
193-
}
193+
}
194+
195+
// https://github.com/microsoft/TypeScript/issues/39357
196+
namespace GH39357 {
197+
type A = ["a", number] | ["b", number] | ["c", string];
198+
type B = "a" | "b" | "c";
199+
declare const b: B;
200+
const a: A = b === "a" || b === "b" ? [b, 1] : ["c", ""];
201+
}

0 commit comments

Comments
 (0)