Skip to content

Commit 864e179

Browse files
authored
Merge pull request microsoft#12396 from Microsoft/getTypeOfExpression
Introduce 'getTypeOfExpression' function
2 parents 680fb2e + c1c12c7 commit 864e179

File tree

5 files changed

+538
-23
lines changed

5 files changed

+538
-23
lines changed

src/compiler/checker.ts

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8975,7 +8975,7 @@ namespace ts {
89758975

89768976
function getTypeWithDefault(type: Type, defaultExpression: Expression) {
89778977
if (defaultExpression) {
8978-
const defaultType = checkExpression(defaultExpression);
8978+
const defaultType = getTypeOfExpression(defaultExpression);
89798979
return getUnionType([getTypeWithFacts(type, TypeFacts.NEUndefined), defaultType]);
89808980
}
89818981
return type;
@@ -9002,7 +9002,7 @@ namespace ts {
90029002
function getAssignedTypeOfBinaryExpression(node: BinaryExpression): Type {
90039003
return node.parent.kind === SyntaxKind.ArrayLiteralExpression || node.parent.kind === SyntaxKind.PropertyAssignment ?
90049004
getTypeWithDefault(getAssignedType(node), node.right) :
9005-
checkExpression(node.right);
9005+
getTypeOfExpression(node.right);
90069006
}
90079007

90089008
function getAssignedTypeOfArrayLiteralElement(node: ArrayLiteralExpression, element: Expression): Type {
@@ -9060,7 +9060,7 @@ namespace ts {
90609060
// from its initializer, we'll already have cached the type. Otherwise we compute it now
90619061
// without caching such that transient types are reflected.
90629062
const links = getNodeLinks(node);
9063-
return links.resolvedType || checkExpression(node);
9063+
return links.resolvedType || getTypeOfExpression(node);
90649064
}
90659065

90669066
function getInitialTypeOfVariableDeclaration(node: VariableDeclaration) {
@@ -9120,7 +9120,7 @@ namespace ts {
91209120

91219121
function getTypeOfSwitchClause(clause: CaseClause | DefaultClause) {
91229122
if (clause.kind === SyntaxKind.CaseClause) {
9123-
const caseType = getRegularTypeOfLiteralType(checkExpression((<CaseClause>clause).expression));
9123+
const caseType = getRegularTypeOfLiteralType(getTypeOfExpression((<CaseClause>clause).expression));
91249124
return isUnitType(caseType) ? caseType : undefined;
91259125
}
91269126
return neverType;
@@ -9225,7 +9225,7 @@ namespace ts {
92259225
// we defer subtype reduction until the evolving array type is finalized into a manifest
92269226
// array type.
92279227
function addEvolvingArrayElementType(evolvingArrayType: EvolvingArrayType, node: Expression): EvolvingArrayType {
9228-
const elementType = getBaseTypeOfLiteralType(checkExpression(node));
9228+
const elementType = getBaseTypeOfLiteralType(getTypeOfExpression(node));
92299229
return isTypeSubsetOf(elementType, evolvingArrayType.elementType) ? evolvingArrayType : getEvolvingArrayType(getUnionType([evolvingArrayType.elementType, elementType]));
92309230
}
92319231

@@ -9286,7 +9286,7 @@ namespace ts {
92869286
(<BinaryExpression>parent.parent).operatorToken.kind === SyntaxKind.EqualsToken &&
92879287
(<BinaryExpression>parent.parent).left === parent &&
92889288
!isAssignmentTarget(parent.parent) &&
9289-
isTypeAnyOrAllConstituentTypesHaveKind(checkExpression((<ElementAccessExpression>parent).argumentExpression), TypeFlags.NumberLike | TypeFlags.Undefined);
9289+
isTypeAnyOrAllConstituentTypesHaveKind(getTypeOfExpression((<ElementAccessExpression>parent).argumentExpression), TypeFlags.NumberLike | TypeFlags.Undefined);
92909290
return isLengthPushOrUnshift || isElementAssignment;
92919291
}
92929292

@@ -9448,7 +9448,7 @@ namespace ts {
94489448
}
94499449
}
94509450
else {
9451-
const indexType = checkExpression((<ElementAccessExpression>(<BinaryExpression>node).left).argumentExpression);
9451+
const indexType = getTypeOfExpression((<ElementAccessExpression>(<BinaryExpression>node).left).argumentExpression);
94529452
if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike | TypeFlags.Undefined)) {
94539453
evolvedType = addEvolvingArrayElementType(evolvedType, (<BinaryExpression>node).right);
94549454
}
@@ -9673,7 +9673,7 @@ namespace ts {
96739673
if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
96749674
assumeTrue = !assumeTrue;
96759675
}
9676-
const valueType = checkExpression(value);
9676+
const valueType = getTypeOfExpression(value);
96779677
if (valueType.flags & TypeFlags.Nullable) {
96789678
if (!strictNullChecks) {
96799679
return type;
@@ -9760,7 +9760,7 @@ namespace ts {
97609760
}
97619761

97629762
// Check that right operand is a function type with a prototype property
9763-
const rightType = checkExpression(expr.right);
9763+
const rightType = getTypeOfExpression(expr.right);
97649764
if (!isTypeSubtypeOf(rightType, globalFunctionType)) {
97659765
return type;
97669766
}
@@ -9901,7 +9901,7 @@ namespace ts {
99019901
location = location.parent;
99029902
}
99039903
if (isPartOfExpression(location) && !isAssignmentTarget(location)) {
9904-
const type = checkExpression(<Expression>location);
9904+
const type = getTypeOfExpression(<Expression>location);
99059905
if (getExportSymbolOfValueSymbolIfExported(getNodeLinks(location).resolvedSymbol) === symbol) {
99069906
return type;
99079907
}
@@ -10769,15 +10769,15 @@ namespace ts {
1076910769

1077010770
// In an assignment expression, the right operand is contextually typed by the type of the left operand.
1077110771
if (node === binaryExpression.right) {
10772-
return checkExpression(binaryExpression.left);
10772+
return getTypeOfExpression(binaryExpression.left);
1077310773
}
1077410774
}
1077510775
else if (operator === SyntaxKind.BarBarToken) {
1077610776
// When an || expression has a contextual type, the operands are contextually typed by that type. When an ||
1077710777
// expression has no contextual type, the right operand is contextually typed by the type of the left operand.
1077810778
let type = getContextualType(binaryExpression);
1077910779
if (!type && node === binaryExpression.right) {
10780-
type = checkExpression(binaryExpression.left);
10780+
type = getTypeOfExpression(binaryExpression.left);
1078110781
}
1078210782
return type;
1078310783
}
@@ -12148,7 +12148,7 @@ namespace ts {
1214812148
if (node.kind === SyntaxKind.ForInStatement &&
1214912149
child === (<ForInStatement>node).statement &&
1215012150
getForInVariableSymbol(<ForInStatement>node) === symbol &&
12151-
hasNumericPropertyNames(checkExpression((<ForInStatement>node).expression))) {
12151+
hasNumericPropertyNames(getTypeOfExpression((<ForInStatement>node).expression))) {
1215212152
return true;
1215312153
}
1215412154
child = node;
@@ -13784,7 +13784,7 @@ namespace ts {
1378413784
if (!node.possiblyExhaustive) {
1378513785
return false;
1378613786
}
13787-
const type = checkExpression(node.expression);
13787+
const type = getTypeOfExpression(node.expression);
1378813788
if (!isLiteralType(type)) {
1378913789
return false;
1379013790
}
@@ -14876,6 +14876,24 @@ namespace ts {
1487614876
return type;
1487714877
}
1487814878

14879+
// Returns the type of an expression. Unlike checkExpression, this function is simply concerned
14880+
// with computing the type and may not fully check all contained sub-expressions for errors.
14881+
function getTypeOfExpression(node: Expression) {
14882+
// Optimize for the common case of a call to a function with a single non-generic call
14883+
// signature where we can just fetch the return type without checking the arguments.
14884+
if (node.kind === SyntaxKind.CallExpression && (<CallExpression>node).expression.kind !== SyntaxKind.SuperKeyword) {
14885+
const funcType = checkNonNullExpression((<CallExpression>node).expression);
14886+
const signature = getSingleCallSignature(funcType);
14887+
if (signature && !signature.typeParameters) {
14888+
return getReturnTypeOfSignature(signature);
14889+
}
14890+
}
14891+
// Otherwise simply call checkExpression. Ideally, the entire family of checkXXX functions
14892+
// should have a parameter that indicates whether full error checking is required such that
14893+
// we can perform the optimizations locally.
14894+
return checkExpression(node);
14895+
}
14896+
1487914897
// Checks an expression and returns its type. The contextualMapper parameter serves two purposes: When
1488014898
// contextualMapper is not undefined and not equal to the identityMapper function object it indicates that the
1488114899
// expression is being inferentially typed (section 4.15.2 in spec) and provides the type mapper to use in
@@ -18258,7 +18276,7 @@ namespace ts {
1825818276
}
1825918277
}
1826018278

18261-
enumType = checkExpression(expression);
18279+
enumType = getTypeOfExpression(expression);
1826218280
// allow references to constant members of other enums
1826318281
if (!(enumType.symbol && (enumType.symbol.flags & SymbolFlags.Enum))) {
1826418282
return undefined;
@@ -19428,7 +19446,7 @@ namespace ts {
1942819446
// fallthrough
1942919447

1943019448
case SyntaxKind.SuperKeyword:
19431-
const type = isPartOfExpression(node) ? checkExpression(<Expression>node) : getTypeFromTypeNode(<TypeNode>node);
19449+
const type = isPartOfExpression(node) ? getTypeOfExpression(<Expression>node) : getTypeFromTypeNode(<TypeNode>node);
1943219450
return type.symbol;
1943319451

1943419452
case SyntaxKind.ThisType:
@@ -19458,7 +19476,7 @@ namespace ts {
1945819476
case SyntaxKind.NumericLiteral:
1945919477
// index access
1946019478
if (node.parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>node.parent).argumentExpression === node) {
19461-
const objectType = checkExpression((<ElementAccessExpression>node.parent).expression);
19479+
const objectType = getTypeOfExpression((<ElementAccessExpression>node.parent).expression);
1946219480
if (objectType === unknownType) return undefined;
1946319481
const apparentType = getApparentType(objectType);
1946419482
if (apparentType === unknownType) return undefined;
@@ -19497,7 +19515,7 @@ namespace ts {
1949719515
}
1949819516

1949919517
if (isPartOfExpression(node)) {
19500-
return getTypeOfExpression(<Expression>node);
19518+
return getRegularTypeOfExpression(<Expression>node);
1950119519
}
1950219520

1950319521
if (isExpressionWithTypeArgumentsInClassExtendsClause(node)) {
@@ -19559,7 +19577,7 @@ namespace ts {
1955919577
// If this is from "for" initializer
1956019578
// for ({a } = elems[0];.....) { }
1956119579
if (expr.parent.kind === SyntaxKind.BinaryExpression) {
19562-
const iteratedType = checkExpression((<BinaryExpression>expr.parent).right);
19580+
const iteratedType = getTypeOfExpression((<BinaryExpression>expr.parent).right);
1956319581
return checkDestructuringAssignment(expr, iteratedType || unknownType);
1956419582
}
1956519583
// If this is from nested object binding pattern
@@ -19589,11 +19607,11 @@ namespace ts {
1958919607
return typeOfObjectLiteral && getPropertyOfType(typeOfObjectLiteral, location.text);
1959019608
}
1959119609

19592-
function getTypeOfExpression(expr: Expression): Type {
19610+
function getRegularTypeOfExpression(expr: Expression): Type {
1959319611
if (isRightSideOfQualifiedNameOrPropertyAccess(expr)) {
1959419612
expr = <Expression>expr.parent;
1959519613
}
19596-
return getRegularTypeOfLiteralType(checkExpression(expr));
19614+
return getRegularTypeOfLiteralType(getTypeOfExpression(expr));
1959719615
}
1959819616

1959919617
/**
@@ -20020,7 +20038,7 @@ namespace ts {
2002020038
}
2002120039

2002220040
function writeTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) {
20023-
const type = getWidenedType(getTypeOfExpression(expr));
20041+
const type = getWidenedType(getRegularTypeOfExpression(expr));
2002420042
getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags);
2002520043
}
2002620044

tests/baselines/reference/ambientRequireFunction.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
const fs = require("fs");
55
>fs : typeof "fs"
6-
>require("fs") : typeof "fs"
6+
>require("fs") : any
77
>require : (moduleName: string) => any
88
>"fs" : "fs"
99

0 commit comments

Comments
 (0)