@@ -11640,27 +11640,14 @@ namespace ts {
11640
11640
11641
11641
// Keep this up-to-date with the same logic within `getApparentTypeOfContextualType`, since they should behave similarly
11642
11642
function findMatchingDiscriminantType(source: Type, target: UnionOrIntersectionType) {
11643
- let match: Type | undefined;
11644
11643
const sourceProperties = getPropertiesOfObjectType(source);
11645
11644
if (sourceProperties) {
11646
11645
const sourcePropertiesFiltered = findDiscriminantProperties(sourceProperties, target);
11647
11646
if (sourcePropertiesFiltered) {
11648
- for (const sourceProperty of sourcePropertiesFiltered) {
11649
- const sourceType = getTypeOfSymbol(sourceProperty);
11650
- for (const type of target.types) {
11651
- const targetType = getTypeOfPropertyOfType(type, sourceProperty.escapedName);
11652
- if (targetType && isRelatedTo(sourceType, targetType)) {
11653
- if (type === match) continue; // Finding multiple fields which discriminate to the same type is fine
11654
- if (match) {
11655
- return undefined;
11656
- }
11657
- match = type;
11658
- }
11659
- }
11660
- }
11647
+ return discriminateTypeByDiscriminableItems(target, map(sourcePropertiesFiltered, p => ([() => getTypeOfSymbol(p), p.escapedName] as [() => Type, __String])), isRelatedTo);
11661
11648
}
11662
11649
}
11663
- return match ;
11650
+ return undefined ;
11664
11651
}
11665
11652
11666
11653
function typeRelatedToEachType(source: Type, target: IntersectionType, reportErrors: boolean): Ternary {
@@ -12475,6 +12462,25 @@ namespace ts {
12475
12462
}
12476
12463
}
12477
12464
12465
+ function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary): Type | undefined;
12466
+ function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary, defaultValue: Type): Type;
12467
+ function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary, defaultValue?: Type) {
12468
+ let match: Type | undefined;
12469
+ for (const [getDiscriminatingType, propertyName] of discriminators) {
12470
+ for (const type of target.types) {
12471
+ const targetType = getTypeOfPropertyOfType(type, propertyName);
12472
+ if (targetType && related(getDiscriminatingType(), targetType)) {
12473
+ if (match) {
12474
+ if (type === match) continue; // Finding multiple fields which discriminate to the same type is fine
12475
+ return defaultValue;
12476
+ }
12477
+ match = type;
12478
+ }
12479
+ }
12480
+ }
12481
+ return match || defaultValue;
12482
+ }
12483
+
12478
12484
/**
12479
12485
* A type is 'weak' if it is an object type with at least one optional property
12480
12486
* and no required properties, call/construct signatures or index signatures
@@ -14187,7 +14193,7 @@ namespace ts {
14187
14193
if ((<TransientSymbol>prop).isDiscriminantProperty === undefined) {
14188
14194
(<TransientSymbol>prop).isDiscriminantProperty = !!((<TransientSymbol>prop).checkFlags & CheckFlags.HasNonUniformType) && isLiteralType(getTypeOfSymbol(prop));
14189
14195
}
14190
- return (<TransientSymbol>prop).isDiscriminantProperty;
14196
+ return !! (<TransientSymbol>prop).isDiscriminantProperty;
14191
14197
}
14192
14198
}
14193
14199
return false;
@@ -16762,43 +16768,53 @@ namespace ts {
16762
16768
case SyntaxKind.FalseKeyword:
16763
16769
case SyntaxKind.NullKeyword:
16764
16770
case SyntaxKind.Identifier:
16771
+ case SyntaxKind.UndefinedKeyword:
16765
16772
return true;
16766
16773
case SyntaxKind.PropertyAccessExpression:
16767
16774
case SyntaxKind.ParenthesizedExpression:
16768
16775
return isPossiblyDiscriminantValue((<PropertyAccessExpression | ParenthesizedExpression>node).expression);
16776
+ case SyntaxKind.JsxExpression:
16777
+ return !(node as JsxExpression).expression || isPossiblyDiscriminantValue((node as JsxExpression).expression!);
16769
16778
}
16770
16779
return false;
16771
16780
}
16772
16781
16782
+ function discriminateContextualTypeByObjectMembers(node: ObjectLiteralExpression, contextualType: UnionType) {
16783
+ return discriminateTypeByDiscriminableItems(contextualType,
16784
+ map(
16785
+ filter(node.properties, p => !!p.symbol && p.kind === SyntaxKind.PropertyAssignment && isPossiblyDiscriminantValue(p.initializer) && isDiscriminantProperty(contextualType, p.symbol.escapedName)),
16786
+ prop => ([() => checkExpression((prop as PropertyAssignment).initializer), prop.symbol.escapedName] as [() => Type, __String])
16787
+ ),
16788
+ isTypeAssignableTo,
16789
+ contextualType
16790
+ );
16791
+ }
16792
+
16793
+ function discriminateContextualTypeByJSXAttributes(node: JsxAttributes, contextualType: UnionType) {
16794
+ return discriminateTypeByDiscriminableItems(contextualType,
16795
+ map(
16796
+ filter(node.properties, p => !!p.symbol && p.kind === SyntaxKind.JsxAttribute && isDiscriminantProperty(contextualType, p.symbol.escapedName) && (!p.initializer || isPossiblyDiscriminantValue(p.initializer))),
16797
+ prop => ([!(prop as JsxAttribute).initializer ? (() => trueType) : (() => checkExpression((prop as JsxAttribute).initializer!)), prop.symbol.escapedName] as [() => Type, __String])
16798
+ ),
16799
+ isTypeAssignableTo,
16800
+ contextualType
16801
+ );
16802
+ }
16803
+
16773
16804
// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
16774
16805
// be "pushed" onto a node using the contextualType property.
16775
16806
function getApparentTypeOfContextualType(node: Expression): Type | undefined {
16776
16807
let contextualType = getContextualType(node);
16777
16808
contextualType = contextualType && mapType(contextualType, getApparentType);
16778
- if (!(contextualType && contextualType.flags & TypeFlags.Union && isObjectLiteralExpression(node))) {
16779
- return contextualType;
16780
- }
16781
- // Keep the below up-to-date with the work done within `isRelatedTo` by `findMatchingDiscriminantType`
16782
- let match: Type | undefined;
16783
- propLoop: for (const prop of node.properties) {
16784
- if (!prop.symbol) continue;
16785
- if (prop.kind !== SyntaxKind.PropertyAssignment) continue;
16786
- if (isPossiblyDiscriminantValue(prop.initializer) && isDiscriminantProperty(contextualType, prop.symbol.escapedName)) {
16787
- const discriminatingType = checkExpression(prop.initializer);
16788
- for (const type of (contextualType as UnionType).types) {
16789
- const targetType = getTypeOfPropertyOfType(type, prop.symbol.escapedName);
16790
- if (targetType && isTypeAssignableTo(discriminatingType, targetType)) {
16791
- if (match) {
16792
- if (type === match) continue; // Finding multiple fields which discriminate to the same type is fine
16793
- match = undefined;
16794
- break propLoop;
16795
- }
16796
- match = type;
16797
- }
16798
- }
16809
+ if (contextualType && contextualType.flags & TypeFlags.Union) {
16810
+ if (isObjectLiteralExpression(node)) {
16811
+ return discriminateContextualTypeByObjectMembers(node, contextualType as UnionType);
16812
+ }
16813
+ else if (isJsxAttributes(node)) {
16814
+ return discriminateContextualTypeByJSXAttributes(node, contextualType as UnionType);
16799
16815
}
16800
16816
}
16801
- return match || contextualType;
16817
+ return contextualType;
16802
16818
}
16803
16819
16804
16820
/**
0 commit comments