Evolving array element ignores contextual type

Control flow analysis can easily hit circularities or exponential
behaviour when requesting the contextual type of an expression. When
adding an element type to an evolving array type, there is no point in
checking the contextual type of the new element type because it is
unknown -- it is exactly the type of the evolving array, which is
in the middle of being found.

Fixes #14628

This is code of the form:

```ts
let x = []
x[0] = { contextual: 'no' }
x[1] = { contextual: 'should not check' }
x[2] = { contextual: 'contextual type' }
// :
// :
```
This commit is contained in:
Nathan Shively-Sanders 2017-04-07 11:10:16 -07:00
parent 3029b8fe38
commit 5c2091ad33

View File

@ -10893,7 +10893,7 @@ namespace ts {
// we defer subtype reduction until the evolving array type is finalized into a manifest
// array type.
function addEvolvingArrayElementType(evolvingArrayType: EvolvingArrayType, node: Expression): EvolvingArrayType {
const elementType = getBaseTypeOfLiteralType(getTypeOfExpression(node));
const elementType = getBaseTypeOfLiteralType(getContextFreeTypeOfExpression(node));
return isTypeSubsetOf(elementType, evolvingArrayType.elementType) ? evolvingArrayType : getEvolvingArrayType(getUnionType([evolvingArrayType.elementType, elementType]));
}
@ -17064,10 +17064,12 @@ namespace ts {
return type;
}
// Returns the type of an expression. Unlike checkExpression, this function is simply concerned
// with computing the type and may not fully check all contained sub-expressions for errors.
// A cache argument of true indicates that if the function performs a full type check, it is ok
// to cache the result.
/**
* Returns the type of an expression. Unlike checkExpression, this function is simply concerned
* with computing the type and may not fully check all contained sub-expressions for errors.
* A cache argument of true indicates that if the function performs a full type check, it is ok
* to cache the result.
*/
function getTypeOfExpression(node: Expression, cache?: boolean) {
// Optimize for the common case of a call to a function with a single non-generic call
// signature where we can just fetch the return type without checking the arguments.
@ -17084,6 +17086,21 @@ namespace ts {
return cache ? checkExpressionCached(node) : checkExpression(node);
}
/**
* Returns the type of an expression. Unlike checkExpression, this function is simply concerned
* with computing the type and may not fully check all contained sub-expressions for errors.
* It is intended for uses where you know there is no contextual type,
* and requesting the contextual type might cause a circularity or other bad behaviour.
* It sets the contextual type of the node to any before calling getTypeOfExpression.
*/
function getContextFreeTypeOfExpression(node: Expression) {
const saveContextualType = node.contextualType;
node.contextualType = anyType;
const type = getTypeOfExpression(node);
node.contextualType = saveContextualType;
return type;
}
// Checks an expression and returns its type. The contextualMapper parameter serves two purposes: When
// contextualMapper is not undefined and not equal to the identityMapper function object it indicates that the
// expression is being inferentially typed (section 4.15.2 in spec) and provides the type mapper to use in