diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b73431f6f5..df312b991c 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1281,7 +1281,8 @@ namespace ts { function isDottedName(node: Expression): boolean { return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || - node.kind === SyntaxKind.PropertyAccessExpression && isDottedName((node).expression); + node.kind === SyntaxKind.PropertyAccessExpression && isDottedName((node).expression) || + node.kind === SyntaxKind.ParenthesizedExpression && isDottedName((node).expression); } function bindExpressionStatement(node: ExpressionStatement): void { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0af56d7f17..bb59a6143f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16852,7 +16852,7 @@ namespace ts { return !!(declaration && ( declaration.kind === SyntaxKind.VariableDeclaration || declaration.kind === SyntaxKind.Parameter || declaration.kind === SyntaxKind.PropertyDeclaration || declaration.kind === SyntaxKind.PropertySignature) && - (declaration as VariableDeclaration | ParameterDeclaration | PropertyDeclaration | PropertySignature).type); + getEffectiveTypeAnnotationNode(declaration as VariableDeclaration | ParameterDeclaration | PropertyDeclaration | PropertySignature)); } function getExplicitTypeOfSymbol(symbol: Symbol) { @@ -16861,11 +16861,11 @@ namespace ts { getTypeOfSymbol(symbol) : undefined; } - function getTypeOfDottedName(node: Expression) { - // We require the dotted function name in an assertion expression to be comprised of identifiers - // that reference function, method, class or value module symbols; or variable, property or - // parameter symbols with declarations that have explicit type annotations. Such references are - // resolvable with no possibility of triggering circularities in control flow analysis. + // We require the dotted function name in an assertion expression to be comprised of identifiers + // that reference function, method, class or value module symbols; or variable, property or + // parameter symbols with declarations that have explicit type annotations. Such references are + // resolvable with no possibility of triggering circularities in control flow analysis. + function getTypeOfDottedName(node: Expression): Type | undefined { switch (node.kind) { case SyntaxKind.Identifier: const symbol = getResolvedSymbol(node); @@ -16874,10 +16874,10 @@ namespace ts { return checkThisExpression(node); case SyntaxKind.PropertyAccessExpression: const type = getTypeOfDottedName((node).expression); - if (type) { - const prop = getPropertyOfType(type, (node).name.escapedText); - return prop && getExplicitTypeOfSymbol(prop); - } + const prop = type && getPropertyOfType(type, (node).name.escapedText); + return prop && getExplicitTypeOfSymbol(prop); + case SyntaxKind.ParenthesizedExpression: + return getTypeOfDottedName((node).expression); } } @@ -16886,7 +16886,9 @@ namespace ts { let signature = links.effectsSignature; if (signature === undefined) { // A call expression parented by an expression statement is a potential assertion. Other call - // expressions are potential type predicate function calls. + // expressions are potential type predicate function calls. In order to avoid triggering + // circularities in control flow analysis, we use getTypeOfDottedName when resolving the call + // target expression of an assertion. const funcType = node.parent.kind === SyntaxKind.ExpressionStatement ? getTypeOfDottedName(node.expression) : node.expression.kind !== SyntaxKind.SuperKeyword ? checkNonNullExpression(node.expression) : undefined; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b27de58134..b4e4769ab4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3518,28 +3518,33 @@ namespace ts { AssertsIdentifier } - export interface ThisTypePredicate { + export interface TypePredicateBase { + kind: TypePredicateKind; + type: Type | undefined; + } + + export interface ThisTypePredicate extends TypePredicateBase { kind: TypePredicateKind.This; parameterName: undefined; parameterIndex: undefined; type: Type; } - export interface IdentifierTypePredicate { + export interface IdentifierTypePredicate extends TypePredicateBase { kind: TypePredicateKind.Identifier; parameterName: string; parameterIndex: number; type: Type; } - export interface AssertsThisTypePredicate { + export interface AssertsThisTypePredicate extends TypePredicateBase { kind: TypePredicateKind.AssertsThis; parameterName: undefined; parameterIndex: undefined; type: Type | undefined; } - export interface AssertsIdentifierTypePredicate { + export interface AssertsIdentifierTypePredicate extends TypePredicateBase { kind: TypePredicateKind.AssertsIdentifier; parameterName: string; parameterIndex: number;