Revert "Allow (non-assert) type predicates to narrow by discriminant" (#57750)

This commit is contained in:
Gabriela Araujo Britto
2024-03-15 11:59:09 -07:00
committed by GitHub
parent e5bf594753
commit b009837298
8 changed files with 435 additions and 62 deletions

View File

@@ -26799,11 +26799,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function hasMatchingArgument(expression: CallExpression | NewExpression, reference: Node) {
if (expression.arguments) {
for (const argument of expression.arguments) {
if (
isOrContainsMatchingReference(reference, argument)
|| optionalChainContainsReference(argument, reference)
|| getCandidateDiscriminantPropertyAccess(argument, reference)
) {
if (isOrContainsMatchingReference(reference, argument) || optionalChainContainsReference(argument, reference)) {
return true;
}
}
@@ -26817,51 +26813,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return false;
}
function getCandidateDiscriminantPropertyAccess(expr: Expression, reference: Node) {
if (isBindingPattern(reference) || isFunctionExpressionOrArrowFunction(reference) || isObjectLiteralMethod(reference)) {
// When the reference is a binding pattern or function or arrow expression, we are narrowing a pesudo-reference in
// getNarrowedTypeOfSymbol. An identifier for a destructuring variable declared in the same binding pattern or
// parameter declared in the same parameter list is a candidate.
if (isIdentifier(expr)) {
const symbol = getResolvedSymbol(expr);
const declaration = symbol.valueDeclaration;
if (declaration && (isBindingElement(declaration) || isParameter(declaration)) && reference === declaration.parent && !declaration.initializer && !declaration.dotDotDotToken) {
return declaration;
}
}
}
else if (isAccessExpression(expr)) {
// An access expression is a candidate if the reference matches the left hand expression.
if (isMatchingReference(reference, expr.expression)) {
return expr;
}
}
else if (isIdentifier(expr)) {
const symbol = getResolvedSymbol(expr);
if (isConstantVariable(symbol)) {
const declaration = symbol.valueDeclaration!;
// Given 'const x = obj.kind', allow 'x' as an alias for 'obj.kind'
if (
isVariableDeclaration(declaration) && !declaration.type && declaration.initializer && isAccessExpression(declaration.initializer) &&
isMatchingReference(reference, declaration.initializer.expression)
) {
return declaration.initializer;
}
// Given 'const { kind: x } = obj', allow 'x' as an alias for 'obj.kind'
if (isBindingElement(declaration) && !declaration.initializer) {
const parent = declaration.parent.parent;
if (
isVariableDeclaration(parent) && !parent.type && parent.initializer && (isIdentifier(parent.initializer) || isAccessExpression(parent.initializer)) &&
isMatchingReference(reference, parent.initializer)
) {
return declaration;
}
}
}
}
return undefined;
}
function getFlowNodeId(flow: FlowNode): number {
if (!flow.id || flow.id < 0) {
flow.id = nextFlowId;
@@ -28232,12 +28183,57 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return result;
}
function getCandidateDiscriminantPropertyAccess(expr: Expression) {
if (isBindingPattern(reference) || isFunctionExpressionOrArrowFunction(reference) || isObjectLiteralMethod(reference)) {
// When the reference is a binding pattern or function or arrow expression, we are narrowing a pesudo-reference in
// getNarrowedTypeOfSymbol. An identifier for a destructuring variable declared in the same binding pattern or
// parameter declared in the same parameter list is a candidate.
if (isIdentifier(expr)) {
const symbol = getResolvedSymbol(expr);
const declaration = symbol.valueDeclaration;
if (declaration && (isBindingElement(declaration) || isParameter(declaration)) && reference === declaration.parent && !declaration.initializer && !declaration.dotDotDotToken) {
return declaration;
}
}
}
else if (isAccessExpression(expr)) {
// An access expression is a candidate if the reference matches the left hand expression.
if (isMatchingReference(reference, expr.expression)) {
return expr;
}
}
else if (isIdentifier(expr)) {
const symbol = getResolvedSymbol(expr);
if (isConstantVariable(symbol)) {
const declaration = symbol.valueDeclaration!;
// Given 'const x = obj.kind', allow 'x' as an alias for 'obj.kind'
if (
isVariableDeclaration(declaration) && !declaration.type && declaration.initializer && isAccessExpression(declaration.initializer) &&
isMatchingReference(reference, declaration.initializer.expression)
) {
return declaration.initializer;
}
// Given 'const { kind: x } = obj', allow 'x' as an alias for 'obj.kind'
if (isBindingElement(declaration) && !declaration.initializer) {
const parent = declaration.parent.parent;
if (
isVariableDeclaration(parent) && !parent.type && parent.initializer && (isIdentifier(parent.initializer) || isAccessExpression(parent.initializer)) &&
isMatchingReference(reference, parent.initializer)
) {
return declaration;
}
}
}
}
return undefined;
}
function getDiscriminantPropertyAccess(expr: Expression, computedType: Type) {
// As long as the computed type is a subset of the declared type, we use the full declared type to detect
// a discriminant property. In cases where the computed type isn't a subset, e.g because of a preceding type
// predicate narrowing, we use the actual computed type.
if (declaredType.flags & TypeFlags.Union || computedType.flags & TypeFlags.Union) {
const access = getCandidateDiscriminantPropertyAccess(expr, reference);
const access = getCandidateDiscriminantPropertyAccess(expr);
if (access) {
const name = getAccessedPropertyName(access);
if (name) {

View File

@@ -3512,7 +3512,8 @@ export function convertJsonOption(
convertJsonOption(opt.element, value, basePath, errors, propertyAssignment, valueExpression, sourceFile);
}
else if (!isString(opt.type)) {
return convertJsonOptionOfCustomType(opt, value as string, errors, valueExpression, sourceFile);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
return convertJsonOptionOfCustomType(opt as CommandLineOptionOfCustomType, value as string, errors, valueExpression, sourceFile);
}
const validatedValue = validateJsonOptionValue(opt, value, errors, valueExpression, sourceFile);
return isNullOrUndefined(validatedValue) ? validatedValue : normalizeNonListOptionValue(opt, basePath, validatedValue);