Cache discriminated contextual types (#58372)

This commit is contained in:
Anders Hejlsberg 2024-04-30 12:40:28 -07:00 committed by GitHub
parent 36146aa2ea
commit 749bd834be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -30789,57 +30789,67 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
function discriminateContextualTypeByObjectMembers(node: ObjectLiteralExpression, contextualType: UnionType) {
return getMatchingUnionConstituentForObjectLiteral(contextualType, node) || discriminateTypeByDiscriminableItems(
contextualType,
concatenate(
map(
filter(node.properties, (p): p is PropertyAssignment | ShorthandPropertyAssignment => {
if (!p.symbol) {
const key = `D${getNodeId(node)},${getTypeId(contextualType)}`;
return getCachedType(key) ?? setCachedType(
key,
getMatchingUnionConstituentForObjectLiteral(contextualType, node) ?? discriminateTypeByDiscriminableItems(
contextualType,
concatenate(
map(
filter(node.properties, (p): p is PropertyAssignment | ShorthandPropertyAssignment => {
if (!p.symbol) {
return false;
}
if (p.kind === SyntaxKind.PropertyAssignment) {
return isPossiblyDiscriminantValue(p.initializer) && isDiscriminantProperty(contextualType, p.symbol.escapedName);
}
if (p.kind === SyntaxKind.ShorthandPropertyAssignment) {
return isDiscriminantProperty(contextualType, p.symbol.escapedName);
}
return false;
}
if (p.kind === SyntaxKind.PropertyAssignment) {
return isPossiblyDiscriminantValue(p.initializer) && isDiscriminantProperty(contextualType, p.symbol.escapedName);
}
if (p.kind === SyntaxKind.ShorthandPropertyAssignment) {
return isDiscriminantProperty(contextualType, p.symbol.escapedName);
}
return false;
}),
prop => ([() => getContextFreeTypeOfExpression(prop.kind === SyntaxKind.PropertyAssignment ? prop.initializer : prop.name), prop.symbol.escapedName] as const),
),
map(
filter(getPropertiesOfType(contextualType), s => !!(s.flags & SymbolFlags.Optional) && !!node?.symbol?.members && !node.symbol.members.has(s.escapedName) && isDiscriminantProperty(contextualType, s.escapedName)),
s => [() => undefinedType, s.escapedName] as const,
}),
prop => ([() => getContextFreeTypeOfExpression(prop.kind === SyntaxKind.PropertyAssignment ? prop.initializer : prop.name), prop.symbol.escapedName] as const),
),
map(
filter(getPropertiesOfType(contextualType), s => !!(s.flags & SymbolFlags.Optional) && !!node?.symbol?.members && !node.symbol.members.has(s.escapedName) && isDiscriminantProperty(contextualType, s.escapedName)),
s => [() => undefinedType, s.escapedName] as const,
),
),
isTypeAssignableTo,
),
isTypeAssignableTo,
);
}
function discriminateContextualTypeByJSXAttributes(node: JsxAttributes, contextualType: UnionType) {
const key = `D${getNodeId(node)},${getTypeId(contextualType)}`;
const cached = getCachedType(key);
if (cached) return cached;
const jsxChildrenPropertyName = getJsxElementChildrenPropertyName(getJsxNamespaceAt(node));
return discriminateTypeByDiscriminableItems(
contextualType,
concatenate(
map(
filter(node.properties, p => !!p.symbol && p.kind === SyntaxKind.JsxAttribute && isDiscriminantProperty(contextualType, p.symbol.escapedName) && (!p.initializer || isPossiblyDiscriminantValue(p.initializer))),
prop => ([!(prop as JsxAttribute).initializer ? (() => trueType) : (() => getContextFreeTypeOfExpression((prop as JsxAttribute).initializer!)), prop.symbol.escapedName] as const),
),
map(
filter(getPropertiesOfType(contextualType), s => {
if (!(s.flags & SymbolFlags.Optional) || !node?.symbol?.members) {
return false;
}
const element = node.parent.parent;
if (s.escapedName === jsxChildrenPropertyName && isJsxElement(element) && getSemanticJsxChildren(element.children).length) {
return false;
}
return !node.symbol.members.has(s.escapedName) && isDiscriminantProperty(contextualType, s.escapedName);
}),
s => [() => undefinedType, s.escapedName] as const,
return setCachedType(
key,
discriminateTypeByDiscriminableItems(
contextualType,
concatenate(
map(
filter(node.properties, p => !!p.symbol && p.kind === SyntaxKind.JsxAttribute && isDiscriminantProperty(contextualType, p.symbol.escapedName) && (!p.initializer || isPossiblyDiscriminantValue(p.initializer))),
prop => ([!(prop as JsxAttribute).initializer ? (() => trueType) : (() => getContextFreeTypeOfExpression((prop as JsxAttribute).initializer!)), prop.symbol.escapedName] as const),
),
map(
filter(getPropertiesOfType(contextualType), s => {
if (!(s.flags & SymbolFlags.Optional) || !node?.symbol?.members) {
return false;
}
const element = node.parent.parent;
if (s.escapedName === jsxChildrenPropertyName && isJsxElement(element) && getSemanticJsxChildren(element.children).length) {
return false;
}
return !node.symbol.members.has(s.escapedName) && isDiscriminantProperty(contextualType, s.escapedName);
}),
s => [() => undefinedType, s.escapedName] as const,
),
),
isTypeAssignableTo,
),
isTypeAssignableTo,
);
}