mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-22 22:55:36 -05:00
Discriminate contextual types (#19733)
* Discriminate contextual types * Invert conditional * Update findMatchingDiscriminantType and baselines
This commit is contained in:
@@ -9269,20 +9269,24 @@ namespace ts {
|
||||
return Ternary.False;
|
||||
}
|
||||
|
||||
// Keep this up-to-date with the same logic within `getApparentTypeOfContextualType`, since they should behave similarly
|
||||
function findMatchingDiscriminantType(source: Type, target: UnionOrIntersectionType) {
|
||||
let match: Type;
|
||||
const sourceProperties = getPropertiesOfObjectType(source);
|
||||
if (sourceProperties) {
|
||||
const sourceProperty = findSingleDiscriminantProperty(sourceProperties, target);
|
||||
if (sourceProperty) {
|
||||
const sourceType = getTypeOfSymbol(sourceProperty);
|
||||
for (const type of target.types) {
|
||||
const targetType = getTypeOfPropertyOfType(type, sourceProperty.escapedName);
|
||||
if (targetType && isRelatedTo(sourceType, targetType)) {
|
||||
if (match) {
|
||||
return undefined;
|
||||
const sourcePropertiesFiltered = findDiscriminantProperties(sourceProperties, target);
|
||||
if (sourcePropertiesFiltered) {
|
||||
for (const sourceProperty of sourcePropertiesFiltered) {
|
||||
const sourceType = getTypeOfSymbol(sourceProperty);
|
||||
for (const type of target.types) {
|
||||
const targetType = getTypeOfPropertyOfType(type, sourceProperty.escapedName);
|
||||
if (targetType && isRelatedTo(sourceType, targetType)) {
|
||||
if (type === match) continue; // Finding multiple fields which discriminate to the same type is fine
|
||||
if (match) {
|
||||
return undefined;
|
||||
}
|
||||
match = type;
|
||||
}
|
||||
match = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11396,14 +11400,15 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
function findSingleDiscriminantProperty(sourceProperties: Symbol[], target: Type): Symbol | undefined {
|
||||
let result: Symbol;
|
||||
function findDiscriminantProperties(sourceProperties: Symbol[], target: Type): Symbol[] | undefined {
|
||||
let result: Symbol[];
|
||||
for (const sourceProperty of sourceProperties) {
|
||||
if (isDiscriminantProperty(target, sourceProperty.escapedName)) {
|
||||
if (result) {
|
||||
return undefined;
|
||||
result.push(sourceProperty);
|
||||
continue;
|
||||
}
|
||||
result = sourceProperty;
|
||||
result = [sourceProperty];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -13691,8 +13696,32 @@ namespace ts {
|
||||
// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
|
||||
// be "pushed" onto a node using the contextualType property.
|
||||
function getApparentTypeOfContextualType(node: Expression): Type {
|
||||
const type = getContextualType(node);
|
||||
return type && mapType(type, getApparentType);
|
||||
let contextualType = getContextualType(node);
|
||||
contextualType = contextualType && mapType(contextualType, getApparentType);
|
||||
if (!(contextualType && contextualType.flags & TypeFlags.Union && isObjectLiteralExpression(node))) {
|
||||
return contextualType;
|
||||
}
|
||||
// Keep the below up-to-date with the work done within `isRelatedTo` by `findMatchingDiscriminantType`
|
||||
let match: Type | undefined;
|
||||
propLoop: for (const prop of node.properties) {
|
||||
if (!prop.symbol) continue;
|
||||
if (prop.kind !== SyntaxKind.PropertyAssignment) continue;
|
||||
if (isDiscriminantProperty(contextualType, prop.symbol.escapedName)) {
|
||||
const discriminatingType = getTypeOfNode(prop.initializer);
|
||||
for (const type of (contextualType as UnionType).types) {
|
||||
const targetType = getTypeOfPropertyOfType(type, prop.symbol.escapedName);
|
||||
if (targetType && checkTypeAssignableTo(discriminatingType, targetType, /*errorNode*/ undefined)) {
|
||||
if (match) {
|
||||
if (type === match) continue; // Finding multiple fields which discriminate to the same type is fine
|
||||
match = undefined;
|
||||
break propLoop;
|
||||
}
|
||||
match = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return match || contextualType;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user