Merge branch 'master' into disambiguating

This commit is contained in:
Yui T
2015-07-28 15:26:31 -07:00
365 changed files with 6559 additions and 6552 deletions

View File

@@ -59,17 +59,21 @@ namespace ts {
isArgumentsSymbol: symbol => symbol === argumentsSymbol,
getDiagnostics,
getGlobalDiagnostics,
getTypeOfSymbolAtLocation,
// The language service will always care about the narrowed type of a symbol, because that is
// the type the language says the symbol should have.
getTypeOfSymbolAtLocation: getNarrowedTypeOfSymbol,
getDeclaredTypeOfSymbol,
getPropertiesOfType,
getPropertyOfType,
getSignaturesOfType,
getIndexTypeOfType,
getBaseTypes,
getReturnTypeOfSignature,
getSymbolsInScope,
getSymbolAtLocation,
getShorthandAssignmentValueSymbol,
getTypeAtLocation,
getTypeAtLocation: getTypeOfNode,
typeToString,
getSymbolDisplayBuilder,
symbolToString,
@@ -159,8 +163,9 @@ namespace ts {
let emitAwaiter = false;
let emitGenerator = false;
let resolutionTargets: Object[] = [];
let resolutionTargets: TypeSystemEntity[] = [];
let resolutionResults: boolean[] = [];
let resolutionPropertyNames: TypeSystemPropertyName[] = [];
let mergedSymbols: Symbol[] = [];
let symbolLinks: SymbolLinks[] = [];
@@ -201,6 +206,15 @@ namespace ts {
let assignableRelation: Map<RelationComparisonResult> = {};
let identityRelation: Map<RelationComparisonResult> = {};
type TypeSystemEntity = Symbol | Type | Signature;
const enum TypeSystemPropertyName {
Type,
ResolvedBaseConstructorType,
DeclaredType,
ResolvedReturnType
}
initializeTypeChecker();
return checker;
@@ -1992,15 +2006,12 @@ namespace ts {
}
return _displayBuilder || (_displayBuilder = {
symbolToString: symbolToString,
typeToString: typeToString,
buildSymbolDisplay: buildSymbolDisplay,
buildTypeDisplay: buildTypeDisplay,
buildTypeParameterDisplay: buildTypeParameterDisplay,
buildParameterDisplay: buildParameterDisplay,
buildDisplayForParametersAndDelimiters: buildDisplayForParametersAndDelimiters,
buildDisplayForTypeParametersAndDelimiters: buildDisplayForTypeParametersAndDelimiters,
buildDisplayForTypeArgumentsAndDelimiters: buildDisplayForTypeArgumentsAndDelimiters,
buildTypeParameterDisplayFromSymbol: buildTypeParameterDisplayFromSymbol,
buildSignatureDisplay: buildSignatureDisplay,
buildReturnTypeDisplay: buildReturnTypeDisplay
@@ -2189,35 +2200,69 @@ namespace ts {
}
}
// Push an entry on the type resolution stack. If an entry with the given target is not already on the stack,
// a new entry with that target and an associated result value of true is pushed on the stack, and the value
// true is returned. Otherwise, a circularity has occurred and the result values of the existing entry and
// all entries pushed after it are changed to false, and the value false is returned. The target object provides
// a unique identity for a particular type resolution result: Symbol instances are used to track resolution of
// SymbolLinks.type, SymbolLinks instances are used to track resolution of SymbolLinks.declaredType, and
// Signature instances are used to track resolution of Signature.resolvedReturnType.
function pushTypeResolution(target: Object): boolean {
let i = 0;
let count = resolutionTargets.length;
while (i < count && resolutionTargets[i] !== target) {
i++;
}
if (i < count) {
do {
resolutionResults[i++] = false;
/**
* Push an entry on the type resolution stack. If an entry with the given target and the given property name
* is already on the stack, and no entries in between already have a type, then a circularity has occurred.
* In this case, the result values of the existing entry and all entries pushed after it are changed to false,
* and the value false is returned. Otherwise, the new entry is just pushed onto the stack, and true is returned.
* In order to see if the same query has already been done before, the target object and the propertyName both
* must match the one passed in.
*
* @param target The symbol, type, or signature whose type is being queried
* @param propertyName The property name that should be used to query the target for its type
*/
function pushTypeResolution(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): boolean {
let resolutionCycleStartIndex = findResolutionCycleStartIndex(target, propertyName);
if (resolutionCycleStartIndex >= 0) {
// A cycle was found
let { length } = resolutionTargets;
for (let i = resolutionCycleStartIndex; i < length; i++) {
resolutionResults[i] = false;
}
while (i < count);
return false;
}
resolutionTargets.push(target);
resolutionResults.push(true);
resolutionPropertyNames.push(propertyName);
return true;
}
function findResolutionCycleStartIndex(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): number {
for (let i = resolutionTargets.length - 1; i >= 0; i--) {
if (hasType(resolutionTargets[i], resolutionPropertyNames[i])) {
return -1;
}
if (resolutionTargets[i] === target && resolutionPropertyNames[i] === propertyName) {
return i;
}
}
return -1;
}
function hasType(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): Type {
if (propertyName === TypeSystemPropertyName.Type) {
return getSymbolLinks(<Symbol>target).type;
}
if (propertyName === TypeSystemPropertyName.DeclaredType) {
return getSymbolLinks(<Symbol>target).declaredType;
}
if (propertyName === TypeSystemPropertyName.ResolvedBaseConstructorType) {
Debug.assert(!!((<Type>target).flags & TypeFlags.Class));
return (<InterfaceType>target).resolvedBaseConstructorType;
}
if (propertyName === TypeSystemPropertyName.ResolvedReturnType) {
return (<Signature>target).resolvedReturnType;
}
Debug.fail("Unhandled TypeSystemPropertyName " + propertyName);
}
// Pop an entry from the type resolution stack and return its associated result value. The result value will
// be true if no circularities were detected, or false if a circularity was found.
function popTypeResolution(): boolean {
resolutionTargets.pop();
resolutionPropertyNames.pop();
return resolutionResults.pop();
}
@@ -2480,7 +2525,7 @@ namespace ts {
return links.type = checkExpression((<ExportAssignment>declaration).expression);
}
// Handle variable, parameter or property
if (!pushTypeResolution(symbol)) {
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return unknownType;
}
let type = getWidenedTypeForVariableLikeDeclaration(<VariableLikeDeclaration>declaration, /*reportErrors*/ true);
@@ -2521,7 +2566,7 @@ namespace ts {
function getTypeOfAccessors(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.type) {
if (!pushTypeResolution(symbol)) {
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return unknownType;
}
let getter = <AccessorDeclaration>getDeclarationOfKind(symbol, SyntaxKind.GetAccessor);
@@ -2737,7 +2782,7 @@ namespace ts {
if (!baseTypeNode) {
return type.resolvedBaseConstructorType = undefinedType;
}
if (!pushTypeResolution(type)) {
if (!pushTypeResolution(type, TypeSystemPropertyName.ResolvedBaseConstructorType)) {
return unknownType;
}
let baseConstructorType = checkExpression(baseTypeNode.expression);
@@ -2864,7 +2909,7 @@ namespace ts {
if (!links.declaredType) {
// Note that we use the links object as the target here because the symbol object is used as the unique
// identity for resolution of the 'type' property in SymbolLinks.
if (!pushTypeResolution(links)) {
if (!pushTypeResolution(symbol, TypeSystemPropertyName.DeclaredType)) {
return unknownType;
}
let declaration = <TypeAliasDeclaration>getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
@@ -3080,44 +3125,57 @@ namespace ts {
setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexType, arrayType.numberIndexType);
}
function signatureListsIdentical(s: Signature[], t: Signature[]): boolean {
if (s.length !== t.length) {
return false;
}
for (let i = 0; i < s.length; i++) {
if (!compareSignatures(s[i], t[i], /*compareReturnTypes*/ false, compareTypes)) {
return false;
function findMatchingSignature(signature: Signature, signatureList: Signature[]): Signature {
for (let s of signatureList) {
// Only signatures with no type parameters may differ in return types
if (compareSignatures(signature, s, /*compareReturnTypes*/ !!signature.typeParameters, compareTypes)) {
return s;
}
}
return true;
}
// If the lists of call or construct signatures in the given types are all identical except for return types,
// and if none of the signatures are generic, return a list of signatures that has substitutes a union of the
// return types of the corresponding signatures in each resulting signature.
function getUnionSignatures(types: Type[], kind: SignatureKind): Signature[] {
let signatureLists = map(types, t => getSignaturesOfType(t, kind));
let signatures = signatureLists[0];
for (let signature of signatures) {
if (signature.typeParameters) {
return emptyArray;
}
}
function findMatchingSignatures(signature: Signature, signatureLists: Signature[][]): Signature[] {
let result: Signature[] = undefined;
for (let i = 1; i < signatureLists.length; i++) {
if (!signatureListsIdentical(signatures, signatureLists[i])) {
return emptyArray;
let match = findMatchingSignature(signature, signatureLists[i]);
if (!match) {
return undefined;
}
if (!result) {
result = [signature];
}
if (match !== signature) {
result.push(match);
}
}
let result = map(signatures, cloneSignature);
for (var i = 0; i < result.length; i++) {
let s = result[i];
// Clear resolved return type we possibly got from cloneSignature
s.resolvedReturnType = undefined;
s.unionSignatures = map(signatureLists, signatures => signatures[i]);
}
return result;
}
// The signatures of a union type are those signatures that are present and identical in each of the
// constituent types, except that non-generic signatures may differ in return types. When signatures
// differ in return types, the resulting return type is the union of the constituent return types.
function getUnionSignatures(types: Type[], kind: SignatureKind): Signature[] {
let signatureLists = map(types, t => getSignaturesOfType(t, kind));
let result: Signature[] = undefined;
for (let source of signatureLists[0]) {
let unionSignatures = findMatchingSignatures(source, signatureLists);
if (unionSignatures) {
let signature: Signature = undefined;
if (unionSignatures.length === 1 || source.typeParameters) {
signature = source;
}
else {
signature = cloneSignature(source);
// Clear resolved return type we possibly got from cloneSignature
signature.resolvedReturnType = undefined;
signature.unionSignatures = unionSignatures;
}
(result || (result = [])).push(signature);
}
}
return result || emptyArray;
}
function getUnionIndexType(types: Type[], kind: IndexKind): Type {
let indexTypes: Type[] = [];
for (let type of types) {
@@ -3275,9 +3333,6 @@ namespace ts {
* type itself. Note that the apparent type of a union type is the union type itself.
*/
function getApparentType(type: Type): Type {
if (type.flags & TypeFlags.Union) {
type = getReducedTypeOfUnionType(<UnionType>type);
}
if (type.flags & TypeFlags.TypeParameter) {
do {
type = getConstraintOfTypeParameter(<TypeParameter>type);
@@ -3382,6 +3437,29 @@ namespace ts {
return undefined;
}
// Check if a property with the given name is known anywhere in the given type. In an object
// type, a property is considered known if the object type is empty, if it has any index
// signatures, or if the property is actually declared in the type. In a union or intersection
// type, a property is considered known if it is known in any constituent type.
function isKnownProperty(type: Type, name: string): boolean {
if (type.flags & TypeFlags.ObjectType && type !== globalObjectType) {
var resolved = resolveStructuredTypeMembers(type);
return !!(resolved.properties.length === 0 ||
resolved.stringIndexType ||
resolved.numberIndexType ||
getPropertyOfType(type, name));
}
if (type.flags & TypeFlags.UnionOrIntersection) {
for (let t of (<UnionOrIntersectionType>type).types) {
if (isKnownProperty(t, name)) {
return true;
}
}
return false;
}
return true;
}
function getSignaturesOfStructuredType(type: Type, kind: SignatureKind): Signature[] {
if (type.flags & TypeFlags.StructuredType) {
let resolved = resolveStructuredTypeMembers(<ObjectType>type);
@@ -3551,7 +3629,7 @@ namespace ts {
function getReturnTypeOfSignature(signature: Signature): Type {
if (!signature.resolvedReturnType) {
if (!pushTypeResolution(signature)) {
if (!pushTypeResolution(signature, TypeSystemPropertyName.ResolvedReturnType)) {
return unknownType;
}
let type: Type;
@@ -3991,26 +4069,79 @@ namespace ts {
}
}
function isSubtypeOfAny(candidate: Type, types: Type[]): boolean {
function isObjectLiteralTypeDuplicateOf(source: ObjectType, target: ObjectType): boolean {
let sourceProperties = getPropertiesOfObjectType(source);
let targetProperties = getPropertiesOfObjectType(target);
if (sourceProperties.length !== targetProperties.length) {
return false;
}
for (let sourceProp of sourceProperties) {
let targetProp = getPropertyOfObjectType(target, sourceProp.name);
if (!targetProp ||
getDeclarationFlagsFromSymbol(targetProp) & (NodeFlags.Private | NodeFlags.Protected) ||
!isTypeDuplicateOf(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp))) {
return false;
}
}
return true;
}
function isTupleTypeDuplicateOf(source: TupleType, target: TupleType): boolean {
let sourceTypes = source.elementTypes;
let targetTypes = target.elementTypes;
if (sourceTypes.length !== targetTypes.length) {
return false;
}
for (var i = 0; i < sourceTypes.length; i++) {
if (!isTypeDuplicateOf(sourceTypes[i], targetTypes[i])) {
return false;
}
}
return true;
}
// Returns true if the source type is a duplicate of the target type. A source type is a duplicate of
// a target type if the the two are identical, with the exception that the source type may have null or
// undefined in places where the target type doesn't. This is by design an asymmetric relationship.
function isTypeDuplicateOf(source: Type, target: Type): boolean {
if (source === target) {
return true;
}
if (source.flags & TypeFlags.Undefined || source.flags & TypeFlags.Null && !(target.flags & TypeFlags.Undefined)) {
return true;
}
if (source.flags & TypeFlags.ObjectLiteral && target.flags & TypeFlags.ObjectType) {
return isObjectLiteralTypeDuplicateOf(<ObjectType>source, <ObjectType>target);
}
if (isArrayType(source) && isArrayType(target)) {
return isTypeDuplicateOf((<TypeReference>source).typeArguments[0], (<TypeReference>target).typeArguments[0]);
}
if (isTupleType(source) && isTupleType(target)) {
return isTupleTypeDuplicateOf(<TupleType>source, <TupleType>target);
}
return isTypeIdenticalTo(source, target);
}
function isTypeDuplicateOfSomeType(candidate: Type, types: Type[]): boolean {
for (let type of types) {
if (candidate !== type && isTypeSubtypeOf(candidate, type)) {
if (candidate !== type && isTypeDuplicateOf(candidate, type)) {
return true;
}
}
return false;
}
function removeSubtypes(types: Type[]) {
function removeDuplicateTypes(types: Type[]) {
let i = types.length;
while (i > 0) {
i--;
if (isSubtypeOfAny(types[i], types)) {
if (isTypeDuplicateOfSomeType(types[i], types)) {
types.splice(i, 1);
}
}
}
function containsTypeAny(types: Type[]) {
function containsTypeAny(types: Type[]): boolean {
for (let type of types) {
if (isTypeAny(type)) {
return true;
@@ -4029,30 +4160,26 @@ namespace ts {
}
}
function compareTypeIds(type1: Type, type2: Type): number {
return type1.id - type2.id;
}
// The noSubtypeReduction flag is there because it isn't possible to always do subtype reduction. The flag
// is true when creating a union type from a type node and when instantiating a union type. In both of those
// cases subtype reduction has to be deferred to properly support recursive union types. For example, a
// type alias of the form "type Item = string | (() => Item)" cannot be reduced during its declaration.
function getUnionType(types: Type[], noSubtypeReduction?: boolean): Type {
// We always deduplicate the constituent type set based on object identity, but we'll also deduplicate
// based on the structure of the types unless the noDeduplication flag is true, which is the case when
// creating a union type from a type node and when instantiating a union type. In both of those cases,
// structural deduplication has to be deferred to properly support recursive union types. For example,
// a type of the form "type Item = string | (() => Item)" cannot be deduplicated during its declaration.
function getUnionType(types: Type[], noDeduplication?: boolean): Type {
if (types.length === 0) {
return emptyObjectType;
}
let typeSet: Type[] = [];
addTypesToSet(typeSet, types, TypeFlags.Union);
typeSet.sort(compareTypeIds);
if (noSubtypeReduction) {
if (containsTypeAny(typeSet)) {
return anyType;
}
if (containsTypeAny(typeSet)) {
return anyType;
}
if (noDeduplication) {
removeAllButLast(typeSet, undefinedType);
removeAllButLast(typeSet, nullType);
}
else {
removeSubtypes(typeSet);
removeDuplicateTypes(typeSet);
}
if (typeSet.length === 1) {
return typeSet[0];
@@ -4062,38 +4189,19 @@ namespace ts {
if (!type) {
type = unionTypes[id] = <UnionType>createObjectType(TypeFlags.Union | getWideningFlagsOfTypes(typeSet));
type.types = typeSet;
type.reducedType = noSubtypeReduction ? undefined : type;
}
return type;
}
// Subtype reduction is basically an optimization we do to avoid excessively large union types, which take longer
// to process and look strange in quick info and error messages. Semantically there is no difference between the
// reduced type and the type itself. So, when we detect a circularity we simply say that the reduced type is the
// type itself.
function getReducedTypeOfUnionType(type: UnionType): Type {
if (!type.reducedType) {
type.reducedType = circularType;
let reducedType = getUnionType(type.types, /*noSubtypeReduction*/ false);
if (type.reducedType === circularType) {
type.reducedType = reducedType;
}
}
else if (type.reducedType === circularType) {
type.reducedType = type;
}
return type.reducedType;
}
function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*noSubtypeReduction*/ true);
links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*noDeduplication*/ true);
}
return links.resolvedType;
}
// We do not perform supertype reduction on intersection types. Intersection types are created only by the &
// We do not perform structural deduplication on intersection types. Intersection types are created only by the &
// type operator and we can't reduce those because we want to support recursive intersection types. For example,
// a type alias of the form "type List<T> = T & { next: List<T> }" cannot be reduced during its declaration.
// Also, unlike union types, the order of the constituent types is preserved in order that overload resolution
@@ -4196,7 +4304,7 @@ namespace ts {
// Callers should first ensure this by calling isTypeNode
case SyntaxKind.Identifier:
case SyntaxKind.QualifiedName:
let symbol = getSymbolInfo(node);
let symbol = getSymbolAtLocation(node);
return symbol && getDeclaredTypeOfSymbol(symbol);
default:
return unknownType;
@@ -4374,7 +4482,7 @@ namespace ts {
return createTupleType(instantiateList((<TupleType>type).elementTypes, mapper, instantiateType));
}
if (type.flags & TypeFlags.Union) {
return getUnionType(instantiateList((<UnionType>type).types, mapper, instantiateType), /*noSubtypeReduction*/ true);
return getUnionType(instantiateList((<UnionType>type).types, mapper, instantiateType), /*noDeduplication*/ true);
}
if (type.flags & TypeFlags.Intersection) {
return getIntersectionType(instantiateList((<IntersectionType>type).types, mapper, instantiateType));
@@ -4519,6 +4627,16 @@ namespace ts {
errorInfo = chainDiagnosticMessages(errorInfo, message, arg0, arg1, arg2);
}
function reportRelationError(message: DiagnosticMessage, source: Type, target: Type) {
let sourceType = typeToString(source);
let targetType = typeToString(target);
if (sourceType === targetType) {
sourceType = typeToString(source, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType);
targetType = typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType);
}
reportError(message || Diagnostics.Type_0_is_not_assignable_to_type_1, sourceType, targetType);
}
// Compare two types and return
// Ternary.True if they are related with no assumptions,
// Ternary.Maybe if they are related with assumptions of other relationships, or
@@ -4538,7 +4656,23 @@ namespace ts {
if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True;
}
}
if (relation !== identityRelation && source.flags & TypeFlags.FreshObjectLiteral) {
if (hasExcessProperties(<FreshObjectLiteralType>source, target, reportErrors)) {
if (reportErrors) {
reportRelationError(headMessage, source, target);
}
return Ternary.False;
}
// Above we check for excess properties with respect to the entire target type. When union
// and intersection types are further deconstructed on the target side, we don't want to
// make the check again (as it might fail for a partial target type). Therefore we obtain
// the regular source type and proceed with that.
source = getRegularTypeOfObjectLiteral(source);
}
let saveErrorInfo = errorInfo;
if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
// We have type references to same target type, see if relationship holds for all type arguments
if (result = typesRelatedTo((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, reportErrors)) {
@@ -4615,18 +4749,22 @@ namespace ts {
}
if (reportErrors) {
headMessage = headMessage || Diagnostics.Type_0_is_not_assignable_to_type_1;
let sourceType = typeToString(source);
let targetType = typeToString(target);
if (sourceType === targetType) {
sourceType = typeToString(source, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType);
targetType = typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType);
}
reportError(headMessage, sourceType, targetType);
reportRelationError(headMessage, source, target);
}
return Ternary.False;
}
function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean {
for (let prop of getPropertiesOfObjectType(source)) {
if (!isKnownProperty(target, prop.name)) {
if (reportErrors) {
reportError(Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, symbolToString(prop), typeToString(target));
}
return true;
}
}
}
function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType): Ternary {
let result = Ternary.True;
let sourceTypes = source.types;
@@ -5323,6 +5461,24 @@ namespace ts {
return !!(type.flags & TypeFlags.Tuple);
}
function getRegularTypeOfObjectLiteral(type: Type): Type {
if (type.flags & TypeFlags.FreshObjectLiteral) {
let regularType = (<FreshObjectLiteralType>type).regularType;
if (!regularType) {
regularType = <ResolvedType>createType((<ResolvedType>type).flags & ~TypeFlags.FreshObjectLiteral);
regularType.symbol = (<ResolvedType>type).symbol;
regularType.members = (<ResolvedType>type).members;
regularType.properties = (<ResolvedType>type).properties;
regularType.callSignatures = (<ResolvedType>type).callSignatures;
regularType.constructSignatures = (<ResolvedType>type).constructSignatures;
regularType.stringIndexType = (<ResolvedType>type).stringIndexType;
regularType.numberIndexType = (<ResolvedType>type).numberIndexType;
}
return regularType;
}
return type;
}
function getWidenedTypeOfObjectLiteral(type: Type): Type {
let properties = getPropertiesOfObjectType(type);
let members: SymbolTable = {};
@@ -5853,47 +6009,6 @@ namespace ts {
}
}
function resolveLocation(node: Node) {
// Resolve location from top down towards node if it is a context sensitive expression
// That helps in making sure not assigning types as any when resolved out of order
let containerNodes: Node[] = [];
for (let parent = node.parent; parent; parent = parent.parent) {
if ((isExpression(parent) || isObjectLiteralMethod(node)) &&
isContextSensitive(<Expression>parent)) {
containerNodes.unshift(parent);
}
}
ts.forEach(containerNodes, node => { getTypeOfNode(node); });
}
function getSymbolAtLocation(node: Node): Symbol {
resolveLocation(node);
return getSymbolInfo(node);
}
function getTypeAtLocation(node: Node): Type {
resolveLocation(node);
return getTypeOfNode(node);
}
function getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type {
resolveLocation(node);
// Get the narrowed type of symbol at given location instead of just getting
// the type of the symbol.
// eg.
// function foo(a: string | number) {
// if (typeof a === "string") {
// a/**/
// }
// }
// getTypeOfSymbol for a would return type of parameter symbol string | number
// Unless we provide location /**/, checker wouldn't know how to narrow the type
// By using getNarrowedTypeOfSymbol would return string since it would be able to narrow
// it by typeguard in the if true condition
return getNarrowedTypeOfSymbol(symbol, node);
}
// Get the narrowed type of a given symbol at a given location
function getNarrowedTypeOfSymbol(symbol: Symbol, node: Node) {
let type = getTypeOfSymbol(symbol);
@@ -6990,7 +7105,7 @@ namespace ts {
let stringIndexType = getIndexType(IndexKind.String);
let numberIndexType = getIndexType(IndexKind.Number);
let result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexType, numberIndexType);
result.flags |= TypeFlags.ObjectLiteral | TypeFlags.ContainsObjectLiteral | (typeFlags & TypeFlags.ContainsUndefinedOrNull);
result.flags |= TypeFlags.ObjectLiteral | TypeFlags.FreshObjectLiteral | TypeFlags.ContainsObjectLiteral | (typeFlags & TypeFlags.ContainsUndefinedOrNull);
return result;
function getIndexType(kind: IndexKind) {
@@ -7221,10 +7336,9 @@ namespace ts {
* For example, in the element <MyClass>, the element instance type is `MyClass` (not `typeof MyClass`).
*/
function getJsxElementInstanceType(node: JsxOpeningLikeElement) {
if (!(getNodeLinks(node).jsxFlags & JsxFlags.ClassElement)) {
// There is no such thing as an instance type for a non-class element
return undefined;
}
// There is no such thing as an instance type for a non-class element. This
// line shouldn't be hit.
Debug.assert(!!(getNodeLinks(node).jsxFlags & JsxFlags.ClassElement), 'Should not call getJsxElementInstanceType on non-class Element');
let classSymbol = getJsxElementTagSymbol(node);
if (classSymbol === unknownSymbol) {
@@ -7247,16 +7361,11 @@ namespace ts {
if (signatures.length === 0) {
// We found no signatures at all, which is an error
error(node.tagName, Diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, getTextOfNode(node.tagName));
return undefined;
return unknownType;
}
}
// Check that the constructor/factory returns an object type
let returnType = getUnionType(signatures.map(s => getReturnTypeOfSignature(s)));
if (!isTypeAny(returnType) && !(returnType.flags & TypeFlags.ObjectType)) {
error(node.tagName, Diagnostics.The_return_type_of_a_JSX_element_constructor_must_return_an_object_type);
return undefined;
}
let returnType = getUnionType(signatures.map(getReturnTypeOfSignature));
// Issue an error if this return type isn't assignable to JSX.ElementClass
let elemClassType = getJsxGlobalElementClassType();
@@ -7317,7 +7426,7 @@ namespace ts {
let elemInstanceType = getJsxElementInstanceType(node);
if (isTypeAny(elemInstanceType)) {
return links.resolvedJsxType = anyType;
return links.resolvedJsxType = elemInstanceType;
}
let propsName = getJsxElementPropertiesName();
@@ -8522,6 +8631,9 @@ namespace ts {
if (!produceDiagnostics) {
for (let candidate of candidates) {
if (hasCorrectArity(node, args, candidate)) {
if (candidate.typeParameters && typeArguments) {
candidate = getSignatureInstantiation(candidate, map(typeArguments, getTypeFromTypeNode));
}
return candidate;
}
}
@@ -8870,7 +8982,7 @@ namespace ts {
}
function checkAssertion(node: AssertionExpression) {
let exprType = checkExpression(node.expression);
let exprType = getRegularTypeOfObjectLiteral(checkExpression(node.expression));
let targetType = getTypeFromTypeNode(node.type);
if (produceDiagnostics && targetType !== unknownType) {
let widenedType = getWidenedType(exprType);
@@ -9695,7 +9807,7 @@ namespace ts {
return getUnionType([leftType, rightType]);
case SyntaxKind.EqualsToken:
checkAssignmentOperator(rightType);
return rightType;
return getRegularTypeOfObjectLiteral(rightType);
case SyntaxKind.CommaToken:
return rightType;
}
@@ -10120,7 +10232,7 @@ namespace ts {
}
else {
checkTypeAssignableTo(typePredicate.type,
getTypeAtLocation(node.parameters[typePredicate.parameterIndex]),
getTypeOfNode(node.parameters[typePredicate.parameterIndex]),
typePredicateNode.type);
}
}
@@ -10331,10 +10443,19 @@ namespace ts {
// TS 1.0 spec (April 2014): 8.3.2
// Constructors of classes with no extends clause may not contain super calls, whereas
// constructors of derived classes must contain at least one super call somewhere in their function body.
if (getClassExtendsHeritageClauseElement(<ClassDeclaration>node.parent)) {
let containingClassDecl = <ClassDeclaration>node.parent;
if (getClassExtendsHeritageClauseElement(containingClassDecl)) {
let containingClassSymbol = getSymbolOfNode(containingClassDecl);
let containingClassInstanceType = <InterfaceType>getDeclaredTypeOfSymbol(containingClassSymbol);
let baseConstructorType = getBaseConstructorTypeOfClass(containingClassInstanceType);
if (containsSuperCall(node.body)) {
// The first statement in the body of a constructor must be a super call if both of the following are true:
if (baseConstructorType === nullType) {
error(node, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null);
}
// The first statement in the body of a constructor (excluding prologue directives) must be a super call
// if both of the following are true:
// - The containing class is a derived class.
// - The constructor declares parameter properties
// or the containing class declares instance member variables with initializers.
@@ -10342,18 +10463,30 @@ namespace ts {
forEach((<ClassDeclaration>node.parent).members, isInstancePropertyWithInitializer) ||
forEach(node.parameters, p => p.flags & (NodeFlags.Public | NodeFlags.Private | NodeFlags.Protected));
// Skip past any prologue directives to find the first statement
// to ensure that it was a super call.
if (superCallShouldBeFirst) {
let statements = (<Block>node.body).statements;
if (!statements.length || statements[0].kind !== SyntaxKind.ExpressionStatement || !isSuperCallExpression((<ExpressionStatement>statements[0]).expression)) {
error(node, Diagnostics.A_super_call_must_be_the_first_statement_in_the_constructor_when_a_class_contains_initialized_properties_or_has_parameter_properties);
let superCallStatement: ExpressionStatement;
for (let statement of statements) {
if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCallExpression((<ExpressionStatement>statement).expression)) {
superCallStatement = <ExpressionStatement>statement;
break;
}
if (!isPrologueDirective(statement)) {
break;
}
}
if (!superCallStatement) {
error(node, Diagnostics.A_super_call_must_be_the_first_statement_in_the_constructor_when_a_class_contains_initialized_properties_or_has_parameter_properties);
}
else {
// In such a required super call, it is a compile-time error for argument expressions to reference this.
markThisReferencesAsErrors((<ExpressionStatement>statements[0]).expression);
markThisReferencesAsErrors(superCallStatement.expression);
}
}
}
else {
else if (baseConstructorType !== nullType) {
error(node, Diagnostics.Constructors_for_derived_classes_must_contain_a_super_call);
}
}
@@ -10744,9 +10877,6 @@ namespace ts {
return;
}
// Exports should be checked only if enclosing module contains both exported and non exported declarations.
// In case if all declarations are non-exported check is unnecessary.
// if localSymbol is defined on node then node itself is exported - check is required
let symbol = node.localSymbol;
if (!symbol) {
@@ -10766,27 +10896,45 @@ namespace ts {
// we use SymbolFlags.ExportValue, SymbolFlags.ExportType and SymbolFlags.ExportNamespace
// to denote disjoint declarationSpaces (without making new enum type).
let exportedDeclarationSpaces: SymbolFlags = 0;
let nonExportedDeclarationSpaces: SymbolFlags = 0;
forEach(symbol.declarations, d => {
let exportedDeclarationSpaces = SymbolFlags.None;
let nonExportedDeclarationSpaces = SymbolFlags.None;
let defaultExportedDeclarationSpaces = SymbolFlags.None;
for (let d of symbol.declarations) {
let declarationSpaces = getDeclarationSpaces(d);
if (getEffectiveDeclarationFlags(d, NodeFlags.Export)) {
exportedDeclarationSpaces |= declarationSpaces;
let effectiveDeclarationFlags = getEffectiveDeclarationFlags(d, NodeFlags.Export | NodeFlags.Default);
if (effectiveDeclarationFlags & NodeFlags.Export) {
if (effectiveDeclarationFlags & NodeFlags.Default) {
defaultExportedDeclarationSpaces |= declarationSpaces;
}
else {
exportedDeclarationSpaces |= declarationSpaces;
}
}
else {
nonExportedDeclarationSpaces |= declarationSpaces;
}
});
}
let commonDeclarationSpace = exportedDeclarationSpaces & nonExportedDeclarationSpaces;
// Spaces for anyting not declared a 'default export'.
let nonDefaultExportedDeclarationSpaces = exportedDeclarationSpaces | nonExportedDeclarationSpaces;
let commonDeclarationSpacesForExportsAndLocals = exportedDeclarationSpaces & nonExportedDeclarationSpaces;
let commonDeclarationSpacesForDefaultAndNonDefault = defaultExportedDeclarationSpaces & nonDefaultExportedDeclarationSpaces;
if (commonDeclarationSpace) {
if (commonDeclarationSpacesForExportsAndLocals || commonDeclarationSpacesForDefaultAndNonDefault) {
// declaration spaces for exported and non-exported declarations intersect
forEach(symbol.declarations, d => {
if (getDeclarationSpaces(d) & commonDeclarationSpace) {
for (let d of symbol.declarations) {
let declarationSpaces = getDeclarationSpaces(d);
// Only error on the declarations that conributed to the intersecting spaces.
if (declarationSpaces & commonDeclarationSpacesForDefaultAndNonDefault) {
error(d.name, Diagnostics.Merged_declaration_0_cannot_include_a_default_export_declaration_Consider_adding_a_separate_export_default_0_declaration_instead, declarationNameToString(d.name));
}
else if (declarationSpaces & commonDeclarationSpacesForExportsAndLocals) {
error(d.name, Diagnostics.Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local, declarationNameToString(d.name));
}
});
}
}
function getDeclarationSpaces(d: Declaration): SymbolFlags {
@@ -12631,28 +12779,7 @@ namespace ts {
}
let initializer = member.initializer;
if (initializer) {
autoValue = getConstantValueForEnumMemberInitializer(initializer);
if (autoValue === undefined) {
if (enumIsConst) {
error(initializer, Diagnostics.In_const_enum_declarations_member_initializer_must_be_constant_expression);
}
else if (!ambient) {
// Only here do we need to check that the initializer is assignable to the enum type.
// If it is a constant value (not undefined), it is syntactically constrained to be a number.
// Also, we do not need to check this for ambients because there is already
// a syntax error if it is not a constant.
checkTypeAssignableTo(checkExpression(initializer), enumType, initializer, /*headMessage*/ undefined);
}
}
else if (enumIsConst) {
if (isNaN(autoValue)) {
error(initializer, Diagnostics.const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN);
}
else if (!isFinite(autoValue)) {
error(initializer, Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value);
}
}
autoValue = computeConstantValueForEnumMemberInitializer(initializer, enumType, enumIsConst, ambient);
}
else if (ambient && !enumIsConst) {
autoValue = undefined;
@@ -12666,8 +12793,36 @@ namespace ts {
nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed;
}
function getConstantValueForEnumMemberInitializer(initializer: Expression): number {
return evalConstant(initializer);
function computeConstantValueForEnumMemberInitializer(initializer: Expression, enumType: Type, enumIsConst: boolean, ambient: boolean): number {
// Controls if error should be reported after evaluation of constant value is completed
// Can be false if another more precise error was already reported during evaluation.
let reportError = true;
let value = evalConstant(initializer);
if (reportError) {
if (value === undefined) {
if (enumIsConst) {
error(initializer, Diagnostics.In_const_enum_declarations_member_initializer_must_be_constant_expression);
}
else if (!ambient) {
// Only here do we need to check that the initializer is assignable to the enum type.
// If it is a constant value (not undefined), it is syntactically constrained to be a number.
// Also, we do not need to check this for ambients because there is already
// a syntax error if it is not a constant.
checkTypeAssignableTo(checkExpression(initializer), enumType, initializer, /*headMessage*/ undefined);
}
}
else if (enumIsConst) {
if (isNaN(value)) {
error(initializer, Diagnostics.const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN);
}
else if (!isFinite(value)) {
error(initializer, Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value);
}
}
}
return value;
function evalConstant(e: Node): number {
switch (e.kind) {
@@ -12776,6 +12931,8 @@ namespace ts {
// illegal case: forward reference
if (!isDefinedBefore(propertyDecl, member)) {
reportError = false;
error(e, Diagnostics.A_member_initializer_in_a_const_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_const_enums);
return undefined;
}
@@ -13742,7 +13899,7 @@ namespace ts {
return undefined;
}
function getSymbolInfo(node: Node) {
function getSymbolAtLocation(node: Node) {
if (isInsideWithStatementBody(node)) {
// We cannot answer semantic questions within a with block, do not proceed any further
return undefined;
@@ -13762,7 +13919,7 @@ namespace ts {
else if (node.parent.kind === SyntaxKind.BindingElement &&
node.parent.parent.kind === SyntaxKind.ObjectBindingPattern &&
node === (<BindingElement>node.parent).propertyName) {
let typeOfPattern = getTypeAtLocation(node.parent.parent);
let typeOfPattern = getTypeOfNode(node.parent.parent);
let propertyDeclaration = typeOfPattern && getPropertyOfType(typeOfPattern, (<Identifier>node).text);
if (propertyDeclaration) {
@@ -13845,24 +14002,24 @@ namespace ts {
}
if (isTypeDeclaration(node)) {
// In this case, we call getSymbolOfNode instead of getSymbolInfo because it is a declaration
// In this case, we call getSymbolOfNode instead of getSymbolAtLocation because it is a declaration
let symbol = getSymbolOfNode(node);
return getDeclaredTypeOfSymbol(symbol);
}
if (isTypeDeclarationName(node)) {
let symbol = getSymbolInfo(node);
let symbol = getSymbolAtLocation(node);
return symbol && getDeclaredTypeOfSymbol(symbol);
}
if (isDeclaration(node)) {
// In this case, we call getSymbolOfNode instead of getSymbolInfo because it is a declaration
// In this case, we call getSymbolOfNode instead of getSymbolAtLocation because it is a declaration
let symbol = getSymbolOfNode(node);
return getTypeOfSymbol(symbol);
}
if (isDeclarationName(node)) {
let symbol = getSymbolInfo(node);
let symbol = getSymbolAtLocation(node);
return symbol && getTypeOfSymbol(symbol);
}
@@ -13871,7 +14028,7 @@ namespace ts {
}
if (isInRightSideOfImportOrExportAssignment(<Identifier>node)) {
let symbol = getSymbolInfo(node);
let symbol = getSymbolAtLocation(node);
let declaredType = symbol && getDeclaredTypeOfSymbol(symbol);
return declaredType !== unknownType ? declaredType : getTypeOfSymbol(symbol);
}
@@ -14040,7 +14197,11 @@ namespace ts {
return true;
}
// const enums and modules that contain only const enums are not considered values from the emit perespective
return target !== unknownSymbol && target && target.flags & SymbolFlags.Value && !isConstEnumOrConstEnumOnlyModule(target);
// unless 'preserveConstEnums' option is set to true
return target !== unknownSymbol &&
target &&
target.flags & SymbolFlags.Value &&
(compilerOptions.preserveConstEnums || !isConstEnumOrConstEnumOnlyModule(target));
}
function isConstEnumOrConstEnumOnlyModule(s: Symbol): boolean {