Skip to content

Commit 8fca332

Browse files
committed
Fix an issue with types possibly being related to discriminated type despite of incompatible variance
1 parent 4c9afe8 commit 8fca332

File tree

5 files changed

+266
-0
lines changed

5 files changed

+266
-0
lines changed

src/compiler/checker.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20367,6 +20367,28 @@ namespace ts {
2036720367
// Compare the remaining non-discriminant properties of each match.
2036820368
let result = Ternary.True;
2036920369
for (const type of matchingTypes) {
20370+
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(type) & ObjectFlags.Reference && (source as TypeReference).target === (type as TypeReference).target &&
20371+
!isTupleType(source) && !(isMarkerType(source) || isMarkerType(type))) {
20372+
// When strictNullChecks is disabled, the element type of the empty array literal is undefinedWideningType,
20373+
// and an empty array literal wouldn't be assignable to a `never[]` without this check.
20374+
if (isEmptyArrayLiteralType(source)) {
20375+
return Ternary.True;
20376+
}
20377+
// We have type references to the same generic type, and the type references are not marker
20378+
// type references (which are intended by be compared structurally). Obtain the variance
20379+
// information for the type parameters and relate the type arguments accordingly.
20380+
const variances = getVariances((source as TypeReference).target);
20381+
// We return Ternary.Maybe for a recursive invocation of getVariances (signalled by emptyArray). This
20382+
// effectively means we measure variance only from type parameter occurrences that aren't nested in
20383+
// recursive instantiations of the generic type.
20384+
if (variances === emptyArray) {
20385+
return Ternary.Unknown;
20386+
}
20387+
if (!typeArgumentsRelatedTo(getTypeArguments(source as TypeReference), getTypeArguments(type as TypeReference), variances, /* reportErrors */ false, IntersectionState.None)) {
20388+
return Ternary.False;
20389+
}
20390+
}
20391+
2037020392
result &= propertiesRelatedTo(source, type, /*reportErrors*/ false, excludedProperties, IntersectionState.None);
2037120393
if (result) {
2037220394
result &= signaturesRelatedTo(source, type, SignatureKind.Call, /*reportStructuralErrors*/ false);
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
tests/cases/compiler/discriminatedUnionWithFailedVarianceCheck.ts(20,3): error TS2322: Type 'ResultOne<StringType>' is not assignable to type 'ResultOne<G> | ResultTwo<G>'.
2+
Type 'ResultOne<StringType>' is not assignable to type 'ResultOne<G>'.
3+
Type 'StringType' is not assignable to type 'G'.
4+
'StringType' is assignable to the constraint of type 'G', but 'G' could be instantiated with a different subtype of constraint 'UnknownType'.
5+
tests/cases/compiler/discriminatedUnionWithFailedVarianceCheck.ts(26,11): error TS2322: Type 'ResultOne<StringType>' is not assignable to type 'ResultOne<G>'.
6+
Type 'StringType' is not assignable to type 'G'.
7+
'StringType' is assignable to the constraint of type 'G', but 'G' could be instantiated with a different subtype of constraint 'UnknownType'.
8+
tests/cases/compiler/discriminatedUnionWithFailedVarianceCheck.ts(27,11): error TS2741: Property 'other' is missing in type 'ResultOne<StringType>' but required in type 'ResultTwo<G>'.
9+
tests/cases/compiler/discriminatedUnionWithFailedVarianceCheck.ts(28,11): error TS2322: Type 'ResultOne<StringType>' is not assignable to type 'ResultOne<G> | ResultTwo<G>'.
10+
Type 'ResultOne<StringType>' is not assignable to type 'ResultOne<G>'.
11+
12+
13+
==== tests/cases/compiler/discriminatedUnionWithFailedVarianceCheck.ts (4 errors) ====
14+
interface StringType { prop: string }
15+
interface UnknownType { prop: unknown }
16+
interface ResultOne<G extends UnknownType> {
17+
type: "one";
18+
value: G["prop"];
19+
}
20+
interface ResultTwo<G extends UnknownType> {
21+
type: "two";
22+
other: G["prop"];
23+
}
24+
25+
// repro #51180
26+
27+
function callback<G extends UnknownType>(): ResultOne<G> | ResultTwo<G> {
28+
const dt: ResultOne<StringType> = {
29+
type: "one",
30+
value: "abc",
31+
};
32+
33+
return dt; // error
34+
~~~~~~~~~~
35+
!!! error TS2322: Type 'ResultOne<StringType>' is not assignable to type 'ResultOne<G> | ResultTwo<G>'.
36+
!!! error TS2322: Type 'ResultOne<StringType>' is not assignable to type 'ResultOne<G>'.
37+
!!! error TS2322: Type 'StringType' is not assignable to type 'G'.
38+
!!! error TS2322: 'StringType' is assignable to the constraint of type 'G', but 'G' could be instantiated with a different subtype of constraint 'UnknownType'.
39+
}
40+
41+
// repro #51180#issuecomment-1279445430
42+
43+
function callback2<G extends UnknownType>(s: ResultOne<StringType>) {
44+
const a1: ResultOne<G> = s; // error
45+
~~
46+
!!! error TS2322: Type 'ResultOne<StringType>' is not assignable to type 'ResultOne<G>'.
47+
!!! error TS2322: Type 'StringType' is not assignable to type 'G'.
48+
!!! error TS2322: 'StringType' is assignable to the constraint of type 'G', but 'G' could be instantiated with a different subtype of constraint 'UnknownType'.
49+
const a2: ResultTwo<G> = s; // error
50+
~~
51+
!!! error TS2741: Property 'other' is missing in type 'ResultOne<StringType>' but required in type 'ResultTwo<G>'.
52+
!!! related TS2728 tests/cases/compiler/discriminatedUnionWithFailedVarianceCheck.ts:9:3: 'other' is declared here.
53+
const m: ResultOne<G> | ResultTwo<G> = s; // error
54+
~
55+
!!! error TS2322: Type 'ResultOne<StringType>' is not assignable to type 'ResultOne<G> | ResultTwo<G>'.
56+
!!! error TS2322: Type 'ResultOne<StringType>' is not assignable to type 'ResultOne<G>'.
57+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
=== tests/cases/compiler/discriminatedUnionWithFailedVarianceCheck.ts ===
2+
interface StringType { prop: string }
3+
>StringType : Symbol(StringType, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 0, 0))
4+
>prop : Symbol(StringType.prop, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 0, 22))
5+
6+
interface UnknownType { prop: unknown }
7+
>UnknownType : Symbol(UnknownType, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 0, 37))
8+
>prop : Symbol(UnknownType.prop, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 1, 23))
9+
10+
interface ResultOne<G extends UnknownType> {
11+
>ResultOne : Symbol(ResultOne, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 1, 39))
12+
>G : Symbol(G, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 2, 20))
13+
>UnknownType : Symbol(UnknownType, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 0, 37))
14+
15+
type: "one";
16+
>type : Symbol(ResultOne.type, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 2, 44))
17+
18+
value: G["prop"];
19+
>value : Symbol(ResultOne.value, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 3, 14))
20+
>G : Symbol(G, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 2, 20))
21+
}
22+
interface ResultTwo<G extends UnknownType> {
23+
>ResultTwo : Symbol(ResultTwo, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 5, 1))
24+
>G : Symbol(G, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 6, 20))
25+
>UnknownType : Symbol(UnknownType, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 0, 37))
26+
27+
type: "two";
28+
>type : Symbol(ResultTwo.type, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 6, 44))
29+
30+
other: G["prop"];
31+
>other : Symbol(ResultTwo.other, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 7, 14))
32+
>G : Symbol(G, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 6, 20))
33+
}
34+
35+
// repro #51180
36+
37+
function callback<G extends UnknownType>(): ResultOne<G> | ResultTwo<G> {
38+
>callback : Symbol(callback, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 9, 1))
39+
>G : Symbol(G, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 13, 18))
40+
>UnknownType : Symbol(UnknownType, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 0, 37))
41+
>ResultOne : Symbol(ResultOne, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 1, 39))
42+
>G : Symbol(G, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 13, 18))
43+
>ResultTwo : Symbol(ResultTwo, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 5, 1))
44+
>G : Symbol(G, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 13, 18))
45+
46+
const dt: ResultOne<StringType> = {
47+
>dt : Symbol(dt, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 14, 7))
48+
>ResultOne : Symbol(ResultOne, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 1, 39))
49+
>StringType : Symbol(StringType, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 0, 0))
50+
51+
type: "one",
52+
>type : Symbol(type, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 14, 37))
53+
54+
value: "abc",
55+
>value : Symbol(value, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 15, 16))
56+
57+
};
58+
59+
return dt; // error
60+
>dt : Symbol(dt, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 14, 7))
61+
}
62+
63+
// repro #51180#issuecomment-1279445430
64+
65+
function callback2<G extends UnknownType>(s: ResultOne<StringType>) {
66+
>callback2 : Symbol(callback2, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 20, 1))
67+
>G : Symbol(G, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 24, 19))
68+
>UnknownType : Symbol(UnknownType, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 0, 37))
69+
>s : Symbol(s, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 24, 42))
70+
>ResultOne : Symbol(ResultOne, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 1, 39))
71+
>StringType : Symbol(StringType, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 0, 0))
72+
73+
const a1: ResultOne<G> = s; // error
74+
>a1 : Symbol(a1, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 25, 9))
75+
>ResultOne : Symbol(ResultOne, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 1, 39))
76+
>G : Symbol(G, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 24, 19))
77+
>s : Symbol(s, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 24, 42))
78+
79+
const a2: ResultTwo<G> = s; // error
80+
>a2 : Symbol(a2, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 26, 9))
81+
>ResultTwo : Symbol(ResultTwo, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 5, 1))
82+
>G : Symbol(G, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 24, 19))
83+
>s : Symbol(s, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 24, 42))
84+
85+
const m: ResultOne<G> | ResultTwo<G> = s; // error
86+
>m : Symbol(m, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 27, 9))
87+
>ResultOne : Symbol(ResultOne, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 1, 39))
88+
>G : Symbol(G, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 24, 19))
89+
>ResultTwo : Symbol(ResultTwo, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 5, 1))
90+
>G : Symbol(G, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 24, 19))
91+
>s : Symbol(s, Decl(discriminatedUnionWithFailedVarianceCheck.ts, 24, 42))
92+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
=== tests/cases/compiler/discriminatedUnionWithFailedVarianceCheck.ts ===
2+
interface StringType { prop: string }
3+
>prop : string
4+
5+
interface UnknownType { prop: unknown }
6+
>prop : unknown
7+
8+
interface ResultOne<G extends UnknownType> {
9+
type: "one";
10+
>type : "one"
11+
12+
value: G["prop"];
13+
>value : G["prop"]
14+
}
15+
interface ResultTwo<G extends UnknownType> {
16+
type: "two";
17+
>type : "two"
18+
19+
other: G["prop"];
20+
>other : G["prop"]
21+
}
22+
23+
// repro #51180
24+
25+
function callback<G extends UnknownType>(): ResultOne<G> | ResultTwo<G> {
26+
>callback : <G extends UnknownType>() => ResultOne<G> | ResultTwo<G>
27+
28+
const dt: ResultOne<StringType> = {
29+
>dt : ResultOne<StringType>
30+
>{ type: "one", value: "abc", } : { type: "one"; value: string; }
31+
32+
type: "one",
33+
>type : "one"
34+
>"one" : "one"
35+
36+
value: "abc",
37+
>value : string
38+
>"abc" : "abc"
39+
40+
};
41+
42+
return dt; // error
43+
>dt : ResultOne<StringType>
44+
}
45+
46+
// repro #51180#issuecomment-1279445430
47+
48+
function callback2<G extends UnknownType>(s: ResultOne<StringType>) {
49+
>callback2 : <G extends UnknownType>(s: ResultOne<StringType>) => void
50+
>s : ResultOne<StringType>
51+
52+
const a1: ResultOne<G> = s; // error
53+
>a1 : ResultOne<G>
54+
>s : ResultOne<StringType>
55+
56+
const a2: ResultTwo<G> = s; // error
57+
>a2 : ResultTwo<G>
58+
>s : ResultOne<StringType>
59+
60+
const m: ResultOne<G> | ResultTwo<G> = s; // error
61+
>m : ResultOne<G> | ResultTwo<G>
62+
>s : ResultOne<StringType>
63+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
interface StringType { prop: string }
5+
interface UnknownType { prop: unknown }
6+
interface ResultOne<G extends UnknownType> {
7+
type: "one";
8+
value: G["prop"];
9+
}
10+
interface ResultTwo<G extends UnknownType> {
11+
type: "two";
12+
other: G["prop"];
13+
}
14+
15+
// repro #51180
16+
17+
function callback<G extends UnknownType>(): ResultOne<G> | ResultTwo<G> {
18+
const dt: ResultOne<StringType> = {
19+
type: "one",
20+
value: "abc",
21+
};
22+
23+
return dt; // error
24+
}
25+
26+
// repro #51180#issuecomment-1279445430
27+
28+
function callback2<G extends UnknownType>(s: ResultOne<StringType>) {
29+
const a1: ResultOne<G> = s; // error
30+
const a2: ResultTwo<G> = s; // error
31+
const m: ResultOne<G> | ResultTwo<G> = s; // error
32+
}

0 commit comments

Comments
 (0)