Skip to content

Commit beebda3

Browse files
authored
Merge pull request #29110 from Microsoft/fixDiscriminantCheck
Fix discriminant property check
2 parents 8570a67 + 017c11a commit beebda3

7 files changed

+235
-36
lines changed

src/compiler/checker.ts

+19-27
Original file line numberDiff line numberDiff line change
@@ -7699,38 +7699,37 @@ namespace ts {
76997699
return props[0];
77007700
}
77017701
let declarations: Declaration[] | undefined;
7702-
let commonType: Type | undefined;
7702+
let firstType: Type | undefined;
77037703
let nameType: Type | undefined;
77047704
const propTypes: Type[] = [];
7705-
let first = true;
7706-
let commonValueDeclaration: Declaration | undefined;
7705+
let firstValueDeclaration: Declaration | undefined;
77077706
let hasNonUniformValueDeclaration = false;
77087707
for (const prop of props) {
7709-
if (!commonValueDeclaration) {
7710-
commonValueDeclaration = prop.valueDeclaration;
7708+
if (!firstValueDeclaration) {
7709+
firstValueDeclaration = prop.valueDeclaration;
77117710
}
7712-
else if (prop.valueDeclaration !== commonValueDeclaration) {
7711+
else if (prop.valueDeclaration !== firstValueDeclaration) {
77137712
hasNonUniformValueDeclaration = true;
77147713
}
77157714
declarations = addRange(declarations, prop.declarations);
77167715
const type = getTypeOfSymbol(prop);
7717-
if (first) {
7718-
commonType = type;
7716+
if (!firstType) {
7717+
firstType = type;
77197718
nameType = prop.nameType;
7720-
first = false;
77217719
}
7722-
else {
7723-
if (type !== commonType) {
7724-
checkFlags |= CheckFlags.HasNonUniformType;
7725-
}
7720+
else if (type !== firstType) {
7721+
checkFlags |= CheckFlags.HasNonUniformType;
7722+
}
7723+
if (isLiteralType(type)) {
7724+
checkFlags |= CheckFlags.HasLiteralType;
77267725
}
77277726
propTypes.push(type);
77287727
}
77297728
addRange(propTypes, indexTypes);
77307729
const result = createSymbol(SymbolFlags.Property | commonFlags, name, syntheticFlag | checkFlags);
77317730
result.containingType = containingType;
7732-
if (!hasNonUniformValueDeclaration && commonValueDeclaration) {
7733-
result.valueDeclaration = commonValueDeclaration;
7731+
if (!hasNonUniformValueDeclaration && firstValueDeclaration) {
7732+
result.valueDeclaration = firstValueDeclaration;
77347733
}
77357734
result.declarations = declarations!;
77367735
result.nameType = nameType;
@@ -14839,25 +14838,18 @@ namespace ts {
1483914838
}
1484014839

1484114840
function isDiscriminantType(type: Type): boolean {
14842-
if (type.flags & TypeFlags.Union) {
14843-
if (type.flags & (TypeFlags.Boolean | TypeFlags.EnumLiteral)) {
14844-
return true;
14845-
}
14846-
let combined = 0;
14847-
for (const t of (<UnionType>type).types) combined |= t.flags;
14848-
if (combined & TypeFlags.Unit && !(combined & TypeFlags.Instantiable)) {
14849-
return true;
14850-
}
14851-
}
14852-
return false;
14841+
return !!(type.flags & TypeFlags.Union &&
14842+
(type.flags & (TypeFlags.Boolean | TypeFlags.EnumLiteral) || !isGenericIndexType(type)));
1485314843
}
1485414844

1485514845
function isDiscriminantProperty(type: Type | undefined, name: __String) {
1485614846
if (type && type.flags & TypeFlags.Union) {
1485714847
const prop = getUnionOrIntersectionProperty(<UnionType>type, name);
1485814848
if (prop && getCheckFlags(prop) & CheckFlags.SyntheticProperty) {
1485914849
if ((<TransientSymbol>prop).isDiscriminantProperty === undefined) {
14860-
(<TransientSymbol>prop).isDiscriminantProperty = !!((<TransientSymbol>prop).checkFlags & CheckFlags.HasNonUniformType) && isDiscriminantType(getTypeOfSymbol(prop));
14850+
(<TransientSymbol>prop).isDiscriminantProperty =
14851+
((<TransientSymbol>prop).checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant &&
14852+
isDiscriminantType(getTypeOfSymbol(prop));
1486114853
}
1486214854
return !!(<TransientSymbol>prop).isDiscriminantProperty;
1486314855
}

src/compiler/types.ts

+11-9
Original file line numberDiff line numberDiff line change
@@ -3677,15 +3677,17 @@ namespace ts {
36773677
Readonly = 1 << 3, // Readonly transient symbol
36783678
Partial = 1 << 4, // Synthetic property present in some but not all constituents
36793679
HasNonUniformType = 1 << 5, // Synthetic property with non-uniform type in constituents
3680-
ContainsPublic = 1 << 6, // Synthetic property with public constituent(s)
3681-
ContainsProtected = 1 << 7, // Synthetic property with protected constituent(s)
3682-
ContainsPrivate = 1 << 8, // Synthetic property with private constituent(s)
3683-
ContainsStatic = 1 << 9, // Synthetic property with static constituent(s)
3684-
Late = 1 << 10, // Late-bound symbol for a computed property with a dynamic name
3685-
ReverseMapped = 1 << 11, // Property of reverse-inferred homomorphic mapped type
3686-
OptionalParameter = 1 << 12, // Optional parameter
3687-
RestParameter = 1 << 13, // Rest parameter
3688-
Synthetic = SyntheticProperty | SyntheticMethod
3680+
HasLiteralType = 1 << 6, // Synthetic property with at least one literal type in constituents
3681+
ContainsPublic = 1 << 7, // Synthetic property with public constituent(s)
3682+
ContainsProtected = 1 << 8, // Synthetic property with protected constituent(s)
3683+
ContainsPrivate = 1 << 9, // Synthetic property with private constituent(s)
3684+
ContainsStatic = 1 << 10, // Synthetic property with static constituent(s)
3685+
Late = 1 << 11, // Late-bound symbol for a computed property with a dynamic name
3686+
ReverseMapped = 1 << 12, // Property of reverse-inferred homomorphic mapped type
3687+
OptionalParameter = 1 << 13, // Optional parameter
3688+
RestParameter = 1 << 14, // Rest parameter
3689+
Synthetic = SyntheticProperty | SyntheticMethod,
3690+
Discriminant = HasNonUniformType | HasLiteralType
36893691
}
36903692

36913693
/* @internal */

tests/baselines/reference/discriminantPropertyCheck.errors.txt

+22
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,26 @@ tests/cases/compiler/discriminantPropertyCheck.ts(65,9): error TS2532: Object is
106106
}
107107
}
108108
}
109+
110+
// Repro from #29106
111+
112+
const f = (_a: string, _b: string): void => {};
113+
114+
interface A {
115+
a?: string;
116+
b?: string;
117+
}
118+
119+
interface B {
120+
a: string;
121+
b: string;
122+
}
123+
124+
type U = A | B;
125+
126+
const u: U = {} as any;
127+
128+
u.a && u.b && f(u.a, u.b);
129+
130+
u.b && u.a && f(u.a, u.b);
109131

tests/baselines/reference/discriminantPropertyCheck.js

+27
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,28 @@ function func2(inst: Instance) {
9898
}
9999
}
100100
}
101+
102+
// Repro from #29106
103+
104+
const f = (_a: string, _b: string): void => {};
105+
106+
interface A {
107+
a?: string;
108+
b?: string;
109+
}
110+
111+
interface B {
112+
a: string;
113+
b: string;
114+
}
115+
116+
type U = A | B;
117+
118+
const u: U = {} as any;
119+
120+
u.a && u.b && f(u.a, u.b);
121+
122+
u.b && u.a && f(u.a, u.b);
101123

102124

103125
//// [discriminantPropertyCheck.js]
@@ -161,3 +183,8 @@ function func2(inst) {
161183
}
162184
}
163185
}
186+
// Repro from #29106
187+
var f = function (_a, _b) { };
188+
var u = {};
189+
u.a && u.b && f(u.a, u.b);
190+
u.b && u.a && f(u.a, u.b);

tests/baselines/reference/discriminantPropertyCheck.symbols

+66
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,69 @@ function func2(inst: Instance) {
311311
}
312312
}
313313

314+
// Repro from #29106
315+
316+
const f = (_a: string, _b: string): void => {};
317+
>f : Symbol(f, Decl(discriminantPropertyCheck.ts, 102, 5))
318+
>_a : Symbol(_a, Decl(discriminantPropertyCheck.ts, 102, 11))
319+
>_b : Symbol(_b, Decl(discriminantPropertyCheck.ts, 102, 22))
320+
321+
interface A {
322+
>A : Symbol(A, Decl(discriminantPropertyCheck.ts, 102, 47))
323+
324+
a?: string;
325+
>a : Symbol(A.a, Decl(discriminantPropertyCheck.ts, 104, 13))
326+
327+
b?: string;
328+
>b : Symbol(A.b, Decl(discriminantPropertyCheck.ts, 105, 13))
329+
}
330+
331+
interface B {
332+
>B : Symbol(B, Decl(discriminantPropertyCheck.ts, 107, 1))
333+
334+
a: string;
335+
>a : Symbol(B.a, Decl(discriminantPropertyCheck.ts, 109, 13))
336+
337+
b: string;
338+
>b : Symbol(B.b, Decl(discriminantPropertyCheck.ts, 110, 12))
339+
}
340+
341+
type U = A | B;
342+
>U : Symbol(U, Decl(discriminantPropertyCheck.ts, 112, 1))
343+
>A : Symbol(A, Decl(discriminantPropertyCheck.ts, 102, 47))
344+
>B : Symbol(B, Decl(discriminantPropertyCheck.ts, 107, 1))
345+
346+
const u: U = {} as any;
347+
>u : Symbol(u, Decl(discriminantPropertyCheck.ts, 116, 5))
348+
>U : Symbol(U, Decl(discriminantPropertyCheck.ts, 112, 1))
349+
350+
u.a && u.b && f(u.a, u.b);
351+
>u.a : Symbol(a, Decl(discriminantPropertyCheck.ts, 104, 13), Decl(discriminantPropertyCheck.ts, 109, 13))
352+
>u : Symbol(u, Decl(discriminantPropertyCheck.ts, 116, 5))
353+
>a : Symbol(a, Decl(discriminantPropertyCheck.ts, 104, 13), Decl(discriminantPropertyCheck.ts, 109, 13))
354+
>u.b : Symbol(b, Decl(discriminantPropertyCheck.ts, 105, 13), Decl(discriminantPropertyCheck.ts, 110, 12))
355+
>u : Symbol(u, Decl(discriminantPropertyCheck.ts, 116, 5))
356+
>b : Symbol(b, Decl(discriminantPropertyCheck.ts, 105, 13), Decl(discriminantPropertyCheck.ts, 110, 12))
357+
>f : Symbol(f, Decl(discriminantPropertyCheck.ts, 102, 5))
358+
>u.a : Symbol(a, Decl(discriminantPropertyCheck.ts, 104, 13), Decl(discriminantPropertyCheck.ts, 109, 13))
359+
>u : Symbol(u, Decl(discriminantPropertyCheck.ts, 116, 5))
360+
>a : Symbol(a, Decl(discriminantPropertyCheck.ts, 104, 13), Decl(discriminantPropertyCheck.ts, 109, 13))
361+
>u.b : Symbol(b, Decl(discriminantPropertyCheck.ts, 105, 13), Decl(discriminantPropertyCheck.ts, 110, 12))
362+
>u : Symbol(u, Decl(discriminantPropertyCheck.ts, 116, 5))
363+
>b : Symbol(b, Decl(discriminantPropertyCheck.ts, 105, 13), Decl(discriminantPropertyCheck.ts, 110, 12))
364+
365+
u.b && u.a && f(u.a, u.b);
366+
>u.b : Symbol(b, Decl(discriminantPropertyCheck.ts, 105, 13), Decl(discriminantPropertyCheck.ts, 110, 12))
367+
>u : Symbol(u, Decl(discriminantPropertyCheck.ts, 116, 5))
368+
>b : Symbol(b, Decl(discriminantPropertyCheck.ts, 105, 13), Decl(discriminantPropertyCheck.ts, 110, 12))
369+
>u.a : Symbol(a, Decl(discriminantPropertyCheck.ts, 104, 13), Decl(discriminantPropertyCheck.ts, 109, 13))
370+
>u : Symbol(u, Decl(discriminantPropertyCheck.ts, 116, 5))
371+
>a : Symbol(a, Decl(discriminantPropertyCheck.ts, 104, 13), Decl(discriminantPropertyCheck.ts, 109, 13))
372+
>f : Symbol(f, Decl(discriminantPropertyCheck.ts, 102, 5))
373+
>u.a : Symbol(a, Decl(discriminantPropertyCheck.ts, 104, 13), Decl(discriminantPropertyCheck.ts, 109, 13))
374+
>u : Symbol(u, Decl(discriminantPropertyCheck.ts, 116, 5))
375+
>a : Symbol(a, Decl(discriminantPropertyCheck.ts, 104, 13), Decl(discriminantPropertyCheck.ts, 109, 13))
376+
>u.b : Symbol(b, Decl(discriminantPropertyCheck.ts, 105, 13), Decl(discriminantPropertyCheck.ts, 110, 12))
377+
>u : Symbol(u, Decl(discriminantPropertyCheck.ts, 116, 5))
378+
>b : Symbol(b, Decl(discriminantPropertyCheck.ts, 105, 13), Decl(discriminantPropertyCheck.ts, 110, 12))
379+

tests/baselines/reference/discriminantPropertyCheck.types

+68
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,71 @@ function func2(inst: Instance) {
310310
}
311311
}
312312

313+
// Repro from #29106
314+
315+
const f = (_a: string, _b: string): void => {};
316+
>f : (_a: string, _b: string) => void
317+
>(_a: string, _b: string): void => {} : (_a: string, _b: string) => void
318+
>_a : string
319+
>_b : string
320+
321+
interface A {
322+
a?: string;
323+
>a : string | undefined
324+
325+
b?: string;
326+
>b : string | undefined
327+
}
328+
329+
interface B {
330+
a: string;
331+
>a : string
332+
333+
b: string;
334+
>b : string
335+
}
336+
337+
type U = A | B;
338+
>U : U
339+
340+
const u: U = {} as any;
341+
>u : U
342+
>{} as any : any
343+
>{} : {}
344+
345+
u.a && u.b && f(u.a, u.b);
346+
>u.a && u.b && f(u.a, u.b) : void | "" | undefined
347+
>u.a && u.b : string | undefined
348+
>u.a : string | undefined
349+
>u : U
350+
>a : string | undefined
351+
>u.b : string | undefined
352+
>u : U
353+
>b : string | undefined
354+
>f(u.a, u.b) : void
355+
>f : (_a: string, _b: string) => void
356+
>u.a : string
357+
>u : U
358+
>a : string
359+
>u.b : string
360+
>u : U
361+
>b : string
362+
363+
u.b && u.a && f(u.a, u.b);
364+
>u.b && u.a && f(u.a, u.b) : void | "" | undefined
365+
>u.b && u.a : string | undefined
366+
>u.b : string | undefined
367+
>u : U
368+
>b : string | undefined
369+
>u.a : string | undefined
370+
>u : U
371+
>a : string | undefined
372+
>f(u.a, u.b) : void
373+
>f : (_a: string, _b: string) => void
374+
>u.a : string
375+
>u : U
376+
>a : string
377+
>u.b : string
378+
>u : U
379+
>b : string
380+

tests/cases/compiler/discriminantPropertyCheck.ts

+22
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,25 @@ function func2(inst: Instance) {
9999
}
100100
}
101101
}
102+
103+
// Repro from #29106
104+
105+
const f = (_a: string, _b: string): void => {};
106+
107+
interface A {
108+
a?: string;
109+
b?: string;
110+
}
111+
112+
interface B {
113+
a: string;
114+
b: string;
115+
}
116+
117+
type U = A | B;
118+
119+
const u: U = {} as any;
120+
121+
u.a && u.b && f(u.a, u.b);
122+
123+
u.b && u.a && f(u.a, u.b);

0 commit comments

Comments
 (0)