From e436a866b5ac0658d79789982fc6141b3c473ddf Mon Sep 17 00:00:00 2001 From: Travis-CI Date: Tue, 9 Aug 2016 00:09:18 +0000 Subject: [PATCH] 2016-08-09 [ci skip] Version: 1.201608090009.1+3f6aa3f3f0705cfc6bfe59920d49e76c12023953 --- TypeScript | 2 +- bin/lib.es2015.core.d.ts | 24 +++++++ bin/lib.es6.d.ts | 24 +++++++ bin/ntypescript.d.ts | 2 + bin/ntypescript.js | 132 ++++++++++++++++++++++++++++++-------- bin/typescript.d.ts | 2 + bin/typescript.js | 132 ++++++++++++++++++++++++++++++-------- kicktravis | 2 +- package.json | 2 +- src/compiler/checker.ts | 135 +++++++++++++++++++++++++++++++-------- src/compiler/types.ts | 2 + 11 files changed, 381 insertions(+), 78 deletions(-) diff --git a/TypeScript b/TypeScript index 269b828..3f6aa3f 160000 --- a/TypeScript +++ b/TypeScript @@ -1 +1 @@ -Subproject commit 269b8285387f5a7fdf1a2d2aafa40e96a425576d +Subproject commit 3f6aa3f3f0705cfc6bfe59920d49e76c12023953 diff --git a/bin/lib.es2015.core.d.ts b/bin/lib.es2015.core.d.ts index c96e103..49c5de6 100644 --- a/bin/lib.es2015.core.d.ts +++ b/bin/lib.es2015.core.d.ts @@ -359,6 +359,30 @@ interface ObjectConstructor { defineProperty(o: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): any; } +interface ReadonlyArray { + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(predicate: (value: T, index: number, obj: ReadonlyArray) => boolean, thisArg?: any): T | undefined; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, + * findIndex immediately returns that element index. Otherwise, findIndex returns -1. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(predicate: (value: T) => boolean, thisArg?: any): number; +} + interface RegExp { /** * Returns a string indicating the flags of the regular expression in question. This field is read-only. diff --git a/bin/lib.es6.d.ts b/bin/lib.es6.d.ts index 12cf047..845fe73 100644 --- a/bin/lib.es6.d.ts +++ b/bin/lib.es6.d.ts @@ -4470,6 +4470,30 @@ interface ObjectConstructor { defineProperty(o: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): any; } +interface ReadonlyArray { + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(predicate: (value: T, index: number, obj: ReadonlyArray) => boolean, thisArg?: any): T | undefined; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, + * findIndex immediately returns that element index. Otherwise, findIndex returns -1. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(predicate: (value: T) => boolean, thisArg?: any): number; +} + interface RegExp { /** * Returns a string indicating the flags of the regular expression in question. This field is read-only. diff --git a/bin/ntypescript.d.ts b/bin/ntypescript.d.ts index 7172cc4..f237b22 100644 --- a/bin/ntypescript.d.ts +++ b/bin/ntypescript.d.ts @@ -1527,6 +1527,8 @@ declare namespace ts { mapper?: TypeMapper; referenced?: boolean; containingType?: UnionOrIntersectionType; + hasCommonType?: boolean; + isDiscriminantProperty?: boolean; resolvedExports?: SymbolTable; exportsChecked?: boolean; isDeclarationWithCollidingName?: boolean; diff --git a/bin/ntypescript.js b/bin/ntypescript.js index fa53bf0..46d399b 100644 --- a/bin/ntypescript.js +++ b/bin/ntypescript.js @@ -20367,11 +20367,20 @@ var ts; } var propTypes = []; var declarations = []; + var commonType = undefined; + var hasCommonType = true; for (var _a = 0, props_1 = props; _a < props_1.length; _a++) { var prop = props_1[_a]; if (prop.declarations) { ts.addRange(declarations, prop.declarations); } + var type = getTypeOfSymbol(prop); + if (!commonType) { + commonType = type; + } + else if (type !== commonType) { + hasCommonType = false; + } propTypes.push(getTypeOfSymbol(prop)); } var result = createSymbol(4 /* Property */ | @@ -20379,6 +20388,7 @@ var ts; 268435456 /* SyntheticProperty */ | commonFlags, name); result.containingType = containingType; + result.hasCommonType = hasCommonType; result.declarations = declarations; result.isReadonly = isReadonly; result.type = containingType.flags & 524288 /* Union */ ? getUnionType(propTypes) : getIntersectionType(propTypes); @@ -20395,9 +20405,14 @@ var ts; } return property; } - // Return the symbol for the property with the given name in the given type. Creates synthetic union properties when - // necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from - // Object and Function as appropriate. + /** + * Return the symbol for the property with the given name in the given type. Creates synthetic union properties when + * necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from + * Object and Function as appropriate. + * + * @param type a type to look up property from + * @param name a name of property to look up in a given type + */ function getPropertyOfType(type, name) { type = getApparentType(type); if (type.flags & 2588672 /* ObjectType */) { @@ -23461,8 +23476,37 @@ var ts; } return false; } - function rootContainsMatchingReference(source, target) { - return target.kind === 172 /* PropertyAccessExpression */ && containsMatchingReference(source, target.expression); + // Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared + // type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property + // a possible discriminant if its type differs in the constituents of containing union type, and if every + // choice is a unit type or a union of unit types. + function containsMatchingReferenceDiscriminant(source, target) { + return target.kind === 172 /* PropertyAccessExpression */ && + containsMatchingReference(source, target.expression) && + isDiscriminantProperty(getDeclaredTypeOfReference(target.expression), target.name.text); + } + function getDeclaredTypeOfReference(expr) { + if (expr.kind === 69 /* Identifier */) { + return getTypeOfSymbol(getResolvedSymbol(expr)); + } + if (expr.kind === 172 /* PropertyAccessExpression */) { + var type = getDeclaredTypeOfReference(expr.expression); + return type && getTypeOfPropertyOfType(type, expr.name.text); + } + return undefined; + } + function isDiscriminantProperty(type, name) { + if (type && type.flags & 524288 /* Union */) { + var prop = getPropertyOfType(type, name); + if (prop && prop.flags & 268435456 /* SyntheticProperty */) { + if (prop.isDiscriminantProperty === undefined) { + prop.isDiscriminantProperty = !prop.hasCommonType && + isUnitUnionType(getTypeOfSymbol(prop)); + } + return prop.isDiscriminantProperty; + } + } + return false; } function isOrContainsMatchingReference(source, target) { return isMatchingReference(source, target) || containsMatchingReference(source, target); @@ -23565,7 +23609,7 @@ var ts; } if (flags & 16384 /* TypeParameter */) { var constraint = getConstraintOfTypeParameter(type); - return constraint ? getTypeFacts(constraint) : 4194303 /* All */; + return getTypeFacts(constraint || emptyObjectType); } if (flags & 1572864 /* UnionOrIntersection */) { return getTypeFactsOfTypes(type.types); @@ -23592,7 +23636,8 @@ var ts; } } } - return firstType ? types ? getUnionType(types) : firstType : neverType; + return types ? getUnionType(types) : + firstType ? firstType : neverType; } function getTypeWithDefault(type, defaultExpression) { if (defaultExpression) { @@ -23728,6 +23773,24 @@ var ts; function eachTypeContainedIn(source, types) { return source.flags & 524288 /* Union */ ? !ts.forEach(source.types, function (t) { return !ts.contains(types, t); }) : ts.contains(types, source); } + function isTypeSubsetOf(source, target) { + return source === target || target.flags & 524288 /* Union */ && isTypeSubsetOfUnion(source, target); + } + function isTypeSubsetOfUnion(source, target) { + if (source.flags & 524288 /* Union */) { + for (var _i = 0, _a = source.types; _i < _a.length; _i++) { + var t = _a[_i]; + if (!containsType(target.types, t)) { + return false; + } + } + return true; + } + if (source.flags & 256 /* EnumLiteral */ && target.flags & 16 /* Enum */ && source.baseType === target) { + return true; + } + return containsType(target.types, source); + } function filterType(type, f) { return type.flags & 524288 /* Union */ ? getUnionType(ts.filter(type.types, f)) : @@ -23858,13 +23921,14 @@ var ts; if (isMatchingReference(reference, expr)) { type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); } - else if (isMatchingPropertyAccess(expr)) { + else if (isMatchingReferenceDiscriminant(expr)) { type = narrowTypeByDiscriminant(type, expr, function (t) { return narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd); }); } return createFlowType(type, isIncomplete(flowType)); } function getTypeAtFlowBranchLabel(flow) { var antecedentTypes = []; + var subtypeReduction = false; var seenIncomplete = false; for (var _i = 0, _a = flow.antecedents; _i < _a.length; _i++) { var antecedent = _a[_i]; @@ -23880,11 +23944,17 @@ var ts; if (!ts.contains(antecedentTypes, type)) { antecedentTypes.push(type); } + // If an antecedent type is not a subset of the declared type, we need to perform + // subtype reduction. This happens when a "foreign" type is injected into the control + // flow using the instanceof operator or a user defined type predicate. + if (!isTypeSubsetOf(type, declaredType)) { + subtypeReduction = true; + } if (isIncomplete(flowType)) { seenIncomplete = true; } } - return createFlowType(getUnionType(antecedentTypes), seenIncomplete); + return createFlowType(getUnionType(antecedentTypes, subtypeReduction), seenIncomplete); } function getTypeAtFlowLoopLabel(flow) { // If we have previously computed the control flow type for the reference at @@ -23909,6 +23979,7 @@ var ts; // Add the flow loop junction and reference to the in-process stack and analyze // each antecedent code path. var antecedentTypes = []; + var subtypeReduction = false; flowLoopNodes[flowLoopCount] = flow; flowLoopKeys[flowLoopCount] = key; flowLoopTypes[flowLoopCount] = antecedentTypes; @@ -23926,6 +23997,12 @@ var ts; if (!ts.contains(antecedentTypes, type)) { antecedentTypes.push(type); } + // If an antecedent type is not a subset of the declared type, we need to perform + // subtype reduction. This happens when a "foreign" type is injected into the control + // flow using the instanceof operator or a user defined type predicate. + if (!isTypeSubsetOf(type, declaredType)) { + subtypeReduction = true; + } // If the type at a particular antecedent path is the declared type there is no // reason to process more antecedents since the only possible outcome is subtypes // that will be removed in the final union type anyway. @@ -23933,12 +24010,13 @@ var ts; break; } } - return cache[key] = getUnionType(antecedentTypes); + return cache[key] = getUnionType(antecedentTypes, subtypeReduction); } - function isMatchingPropertyAccess(expr) { + function isMatchingReferenceDiscriminant(expr) { return expr.kind === 172 /* PropertyAccessExpression */ && + declaredType.flags & 524288 /* Union */ && isMatchingReference(reference, expr.expression) && - (declaredType.flags & 524288 /* Union */) !== 0; + isDiscriminantProperty(declaredType, expr.name.text); } function narrowTypeByDiscriminant(type, propAccess, narrowType) { var propName = propAccess.name.text; @@ -23950,10 +24028,10 @@ var ts; if (isMatchingReference(reference, expr)) { return getTypeWithFacts(type, assumeTrue ? 1048576 /* Truthy */ : 2097152 /* Falsy */); } - if (isMatchingPropertyAccess(expr)) { + if (isMatchingReferenceDiscriminant(expr)) { return narrowTypeByDiscriminant(type, expr, function (t) { return getTypeWithFacts(t, assumeTrue ? 1048576 /* Truthy */ : 2097152 /* Falsy */); }); } - if (rootContainsMatchingReference(reference, expr)) { + if (containsMatchingReferenceDiscriminant(reference, expr)) { return declaredType; } return type; @@ -23981,13 +24059,13 @@ var ts; if (isMatchingReference(reference, right_1)) { return narrowTypeByEquality(type, operator_1, left_1, assumeTrue); } - if (isMatchingPropertyAccess(left_1)) { + if (isMatchingReferenceDiscriminant(left_1)) { return narrowTypeByDiscriminant(type, left_1, function (t) { return narrowTypeByEquality(t, operator_1, right_1, assumeTrue); }); } - if (isMatchingPropertyAccess(right_1)) { + if (isMatchingReferenceDiscriminant(right_1)) { return narrowTypeByDiscriminant(type, right_1, function (t) { return narrowTypeByEquality(t, operator_1, left_1, assumeTrue); }); } - if (rootContainsMatchingReference(reference, left_1) || rootContainsMatchingReference(reference, right_1)) { + if (containsMatchingReferenceDiscriminant(reference, left_1) || containsMatchingReferenceDiscriminant(reference, right_1)) { return declaredType; } break; @@ -24116,9 +24194,7 @@ var ts; } function getNarrowedType(type, candidate, assumeTrue) { if (!assumeTrue) { - return type.flags & 524288 /* Union */ ? - getUnionType(ts.filter(type.types, function (t) { return !isTypeSubtypeOf(t, candidate); })) : - type; + return filterType(type, function (t) { return !isTypeSubtypeOf(t, candidate); }); } // If the current type is a union type, remove all constituents that aren't assignable to // the candidate type. If one or more constituents remain, return a union of those. @@ -24128,13 +24204,16 @@ var ts; return getUnionType(assignableConstituents); } } - // If the candidate type is assignable to the target type, narrow to the candidate type. - // Otherwise, if the current type is assignable to the candidate, keep the current type. - // Otherwise, the types are completely unrelated, so narrow to the empty type. + // If the candidate type is a subtype of the target type, narrow to the candidate type. + // Otherwise, if the target type is assignable to the candidate type, keep the target type. + // Otherwise, if the candidate type is assignable to the target type, narrow to the candidate + // type. Otherwise, the types are completely unrelated, so narrow to an intersection of the + // two types. var targetType = type.flags & 16384 /* TypeParameter */ ? getApparentType(type) : type; - return isTypeAssignableTo(candidate, targetType) ? candidate : + return isTypeSubtypeOf(candidate, targetType) ? candidate : isTypeAssignableTo(type, candidate) ? type : - getIntersectionType([type, candidate]); + isTypeAssignableTo(candidate, targetType) ? candidate : + getIntersectionType([type, candidate]); } function narrowTypeByTypePredicate(type, callExpression, assumeTrue) { if (type.flags & 1 /* Any */ || !hasMatchingArgument(callExpression, reference)) { @@ -28034,6 +28113,9 @@ var ts; return checkDestructuringAssignment(element, type, contextualMapper); } else { + // We still need to check element expression here because we may need to set appropriate flag on the expression + // such as NodeCheckFlags.LexicalThis on "this"expression. + checkExpression(element); if (isTupleType(sourceType)) { error(element, ts.Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(sourceType), sourceType.elementTypes.length, elements.length); } diff --git a/bin/typescript.d.ts b/bin/typescript.d.ts index 785b13e..fb51f48 100644 --- a/bin/typescript.d.ts +++ b/bin/typescript.d.ts @@ -1527,6 +1527,8 @@ declare namespace ts { mapper?: TypeMapper; referenced?: boolean; containingType?: UnionOrIntersectionType; + hasCommonType?: boolean; + isDiscriminantProperty?: boolean; resolvedExports?: SymbolTable; exportsChecked?: boolean; isDeclarationWithCollidingName?: boolean; diff --git a/bin/typescript.js b/bin/typescript.js index a42fa95..912e6c7 100644 --- a/bin/typescript.js +++ b/bin/typescript.js @@ -20367,11 +20367,20 @@ var ts; } var propTypes = []; var declarations = []; + var commonType = undefined; + var hasCommonType = true; for (var _a = 0, props_1 = props; _a < props_1.length; _a++) { var prop = props_1[_a]; if (prop.declarations) { ts.addRange(declarations, prop.declarations); } + var type = getTypeOfSymbol(prop); + if (!commonType) { + commonType = type; + } + else if (type !== commonType) { + hasCommonType = false; + } propTypes.push(getTypeOfSymbol(prop)); } var result = createSymbol(4 /* Property */ | @@ -20379,6 +20388,7 @@ var ts; 268435456 /* SyntheticProperty */ | commonFlags, name); result.containingType = containingType; + result.hasCommonType = hasCommonType; result.declarations = declarations; result.isReadonly = isReadonly; result.type = containingType.flags & 524288 /* Union */ ? getUnionType(propTypes) : getIntersectionType(propTypes); @@ -20395,9 +20405,14 @@ var ts; } return property; } - // Return the symbol for the property with the given name in the given type. Creates synthetic union properties when - // necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from - // Object and Function as appropriate. + /** + * Return the symbol for the property with the given name in the given type. Creates synthetic union properties when + * necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from + * Object and Function as appropriate. + * + * @param type a type to look up property from + * @param name a name of property to look up in a given type + */ function getPropertyOfType(type, name) { type = getApparentType(type); if (type.flags & 2588672 /* ObjectType */) { @@ -23461,8 +23476,37 @@ var ts; } return false; } - function rootContainsMatchingReference(source, target) { - return target.kind === 172 /* PropertyAccessExpression */ && containsMatchingReference(source, target.expression); + // Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared + // type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property + // a possible discriminant if its type differs in the constituents of containing union type, and if every + // choice is a unit type or a union of unit types. + function containsMatchingReferenceDiscriminant(source, target) { + return target.kind === 172 /* PropertyAccessExpression */ && + containsMatchingReference(source, target.expression) && + isDiscriminantProperty(getDeclaredTypeOfReference(target.expression), target.name.text); + } + function getDeclaredTypeOfReference(expr) { + if (expr.kind === 69 /* Identifier */) { + return getTypeOfSymbol(getResolvedSymbol(expr)); + } + if (expr.kind === 172 /* PropertyAccessExpression */) { + var type = getDeclaredTypeOfReference(expr.expression); + return type && getTypeOfPropertyOfType(type, expr.name.text); + } + return undefined; + } + function isDiscriminantProperty(type, name) { + if (type && type.flags & 524288 /* Union */) { + var prop = getPropertyOfType(type, name); + if (prop && prop.flags & 268435456 /* SyntheticProperty */) { + if (prop.isDiscriminantProperty === undefined) { + prop.isDiscriminantProperty = !prop.hasCommonType && + isUnitUnionType(getTypeOfSymbol(prop)); + } + return prop.isDiscriminantProperty; + } + } + return false; } function isOrContainsMatchingReference(source, target) { return isMatchingReference(source, target) || containsMatchingReference(source, target); @@ -23565,7 +23609,7 @@ var ts; } if (flags & 16384 /* TypeParameter */) { var constraint = getConstraintOfTypeParameter(type); - return constraint ? getTypeFacts(constraint) : 4194303 /* All */; + return getTypeFacts(constraint || emptyObjectType); } if (flags & 1572864 /* UnionOrIntersection */) { return getTypeFactsOfTypes(type.types); @@ -23592,7 +23636,8 @@ var ts; } } } - return firstType ? types ? getUnionType(types) : firstType : neverType; + return types ? getUnionType(types) : + firstType ? firstType : neverType; } function getTypeWithDefault(type, defaultExpression) { if (defaultExpression) { @@ -23728,6 +23773,24 @@ var ts; function eachTypeContainedIn(source, types) { return source.flags & 524288 /* Union */ ? !ts.forEach(source.types, function (t) { return !ts.contains(types, t); }) : ts.contains(types, source); } + function isTypeSubsetOf(source, target) { + return source === target || target.flags & 524288 /* Union */ && isTypeSubsetOfUnion(source, target); + } + function isTypeSubsetOfUnion(source, target) { + if (source.flags & 524288 /* Union */) { + for (var _i = 0, _a = source.types; _i < _a.length; _i++) { + var t = _a[_i]; + if (!containsType(target.types, t)) { + return false; + } + } + return true; + } + if (source.flags & 256 /* EnumLiteral */ && target.flags & 16 /* Enum */ && source.baseType === target) { + return true; + } + return containsType(target.types, source); + } function filterType(type, f) { return type.flags & 524288 /* Union */ ? getUnionType(ts.filter(type.types, f)) : @@ -23858,13 +23921,14 @@ var ts; if (isMatchingReference(reference, expr)) { type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); } - else if (isMatchingPropertyAccess(expr)) { + else if (isMatchingReferenceDiscriminant(expr)) { type = narrowTypeByDiscriminant(type, expr, function (t) { return narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd); }); } return createFlowType(type, isIncomplete(flowType)); } function getTypeAtFlowBranchLabel(flow) { var antecedentTypes = []; + var subtypeReduction = false; var seenIncomplete = false; for (var _i = 0, _a = flow.antecedents; _i < _a.length; _i++) { var antecedent = _a[_i]; @@ -23880,11 +23944,17 @@ var ts; if (!ts.contains(antecedentTypes, type)) { antecedentTypes.push(type); } + // If an antecedent type is not a subset of the declared type, we need to perform + // subtype reduction. This happens when a "foreign" type is injected into the control + // flow using the instanceof operator or a user defined type predicate. + if (!isTypeSubsetOf(type, declaredType)) { + subtypeReduction = true; + } if (isIncomplete(flowType)) { seenIncomplete = true; } } - return createFlowType(getUnionType(antecedentTypes), seenIncomplete); + return createFlowType(getUnionType(antecedentTypes, subtypeReduction), seenIncomplete); } function getTypeAtFlowLoopLabel(flow) { // If we have previously computed the control flow type for the reference at @@ -23909,6 +23979,7 @@ var ts; // Add the flow loop junction and reference to the in-process stack and analyze // each antecedent code path. var antecedentTypes = []; + var subtypeReduction = false; flowLoopNodes[flowLoopCount] = flow; flowLoopKeys[flowLoopCount] = key; flowLoopTypes[flowLoopCount] = antecedentTypes; @@ -23926,6 +23997,12 @@ var ts; if (!ts.contains(antecedentTypes, type)) { antecedentTypes.push(type); } + // If an antecedent type is not a subset of the declared type, we need to perform + // subtype reduction. This happens when a "foreign" type is injected into the control + // flow using the instanceof operator or a user defined type predicate. + if (!isTypeSubsetOf(type, declaredType)) { + subtypeReduction = true; + } // If the type at a particular antecedent path is the declared type there is no // reason to process more antecedents since the only possible outcome is subtypes // that will be removed in the final union type anyway. @@ -23933,12 +24010,13 @@ var ts; break; } } - return cache[key] = getUnionType(antecedentTypes); + return cache[key] = getUnionType(antecedentTypes, subtypeReduction); } - function isMatchingPropertyAccess(expr) { + function isMatchingReferenceDiscriminant(expr) { return expr.kind === 172 /* PropertyAccessExpression */ && + declaredType.flags & 524288 /* Union */ && isMatchingReference(reference, expr.expression) && - (declaredType.flags & 524288 /* Union */) !== 0; + isDiscriminantProperty(declaredType, expr.name.text); } function narrowTypeByDiscriminant(type, propAccess, narrowType) { var propName = propAccess.name.text; @@ -23950,10 +24028,10 @@ var ts; if (isMatchingReference(reference, expr)) { return getTypeWithFacts(type, assumeTrue ? 1048576 /* Truthy */ : 2097152 /* Falsy */); } - if (isMatchingPropertyAccess(expr)) { + if (isMatchingReferenceDiscriminant(expr)) { return narrowTypeByDiscriminant(type, expr, function (t) { return getTypeWithFacts(t, assumeTrue ? 1048576 /* Truthy */ : 2097152 /* Falsy */); }); } - if (rootContainsMatchingReference(reference, expr)) { + if (containsMatchingReferenceDiscriminant(reference, expr)) { return declaredType; } return type; @@ -23981,13 +24059,13 @@ var ts; if (isMatchingReference(reference, right_1)) { return narrowTypeByEquality(type, operator_1, left_1, assumeTrue); } - if (isMatchingPropertyAccess(left_1)) { + if (isMatchingReferenceDiscriminant(left_1)) { return narrowTypeByDiscriminant(type, left_1, function (t) { return narrowTypeByEquality(t, operator_1, right_1, assumeTrue); }); } - if (isMatchingPropertyAccess(right_1)) { + if (isMatchingReferenceDiscriminant(right_1)) { return narrowTypeByDiscriminant(type, right_1, function (t) { return narrowTypeByEquality(t, operator_1, left_1, assumeTrue); }); } - if (rootContainsMatchingReference(reference, left_1) || rootContainsMatchingReference(reference, right_1)) { + if (containsMatchingReferenceDiscriminant(reference, left_1) || containsMatchingReferenceDiscriminant(reference, right_1)) { return declaredType; } break; @@ -24116,9 +24194,7 @@ var ts; } function getNarrowedType(type, candidate, assumeTrue) { if (!assumeTrue) { - return type.flags & 524288 /* Union */ ? - getUnionType(ts.filter(type.types, function (t) { return !isTypeSubtypeOf(t, candidate); })) : - type; + return filterType(type, function (t) { return !isTypeSubtypeOf(t, candidate); }); } // If the current type is a union type, remove all constituents that aren't assignable to // the candidate type. If one or more constituents remain, return a union of those. @@ -24128,13 +24204,16 @@ var ts; return getUnionType(assignableConstituents); } } - // If the candidate type is assignable to the target type, narrow to the candidate type. - // Otherwise, if the current type is assignable to the candidate, keep the current type. - // Otherwise, the types are completely unrelated, so narrow to the empty type. + // If the candidate type is a subtype of the target type, narrow to the candidate type. + // Otherwise, if the target type is assignable to the candidate type, keep the target type. + // Otherwise, if the candidate type is assignable to the target type, narrow to the candidate + // type. Otherwise, the types are completely unrelated, so narrow to an intersection of the + // two types. var targetType = type.flags & 16384 /* TypeParameter */ ? getApparentType(type) : type; - return isTypeAssignableTo(candidate, targetType) ? candidate : + return isTypeSubtypeOf(candidate, targetType) ? candidate : isTypeAssignableTo(type, candidate) ? type : - getIntersectionType([type, candidate]); + isTypeAssignableTo(candidate, targetType) ? candidate : + getIntersectionType([type, candidate]); } function narrowTypeByTypePredicate(type, callExpression, assumeTrue) { if (type.flags & 1 /* Any */ || !hasMatchingArgument(callExpression, reference)) { @@ -28034,6 +28113,9 @@ var ts; return checkDestructuringAssignment(element, type, contextualMapper); } else { + // We still need to check element expression here because we may need to set appropriate flag on the expression + // such as NodeCheckFlags.LexicalThis on "this"expression. + checkExpression(element); if (isTupleType(sourceType)) { error(element, ts.Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(sourceType), sourceType.elementTypes.length, elements.length); } diff --git a/kicktravis b/kicktravis index a599263..e0160ef 100644 --- a/kicktravis +++ b/kicktravis @@ -1 +1 @@ -2016-08-08 [ci skip] Version: 1.201608080007.1+269b8285387f5a7fdf1a2d2aafa40e96a425576d +2016-08-09 [ci skip] Version: 1.201608090009.1+3f6aa3f3f0705cfc6bfe59920d49e76c12023953 diff --git a/package.json b/package.json index 9f160dc..9e28bce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ntypescript", - "version": "1.201608080007.1+269b8285387f5a7fdf1a2d2aafa40e96a425576d", + "version": "1.201608090009.1+3f6aa3f3f0705cfc6bfe59920d49e76c12023953", "description": "A nicer version of microsoft/typescript packaged and released for API developers", "main": "./bin/ntypescript.js", "bin": { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9583fad..bec8686 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4416,10 +4416,19 @@ namespace ts { } const propTypes: Type[] = []; const declarations: Declaration[] = []; + let commonType: Type = undefined; + let hasCommonType = true; for (const prop of props) { if (prop.declarations) { addRange(declarations, prop.declarations); } + const type = getTypeOfSymbol(prop); + if (!commonType) { + commonType = type; + } + else if (type !== commonType) { + hasCommonType = false; + } propTypes.push(getTypeOfSymbol(prop)); } const result = createSymbol( @@ -4429,6 +4438,7 @@ namespace ts { commonFlags, name); result.containingType = containingType; + result.hasCommonType = hasCommonType; result.declarations = declarations; result.isReadonly = isReadonly; result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes); @@ -4447,9 +4457,14 @@ namespace ts { return property; } - // Return the symbol for the property with the given name in the given type. Creates synthetic union properties when - // necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from - // Object and Function as appropriate. + /** + * Return the symbol for the property with the given name in the given type. Creates synthetic union properties when + * necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from + * Object and Function as appropriate. + * + * @param type a type to look up property from + * @param name a name of property to look up in a given type + */ function getPropertyOfType(type: Type, name: string): Symbol { type = getApparentType(type); if (type.flags & TypeFlags.ObjectType) { @@ -7793,8 +7808,39 @@ namespace ts { return false; } - function rootContainsMatchingReference(source: Node, target: Node) { - return target.kind === SyntaxKind.PropertyAccessExpression && containsMatchingReference(source, (target).expression); + // Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared + // type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property + // a possible discriminant if its type differs in the constituents of containing union type, and if every + // choice is a unit type or a union of unit types. + function containsMatchingReferenceDiscriminant(source: Node, target: Node) { + return target.kind === SyntaxKind.PropertyAccessExpression && + containsMatchingReference(source, (target).expression) && + isDiscriminantProperty(getDeclaredTypeOfReference((target).expression), (target).name.text); + } + + function getDeclaredTypeOfReference(expr: Node): Type { + if (expr.kind === SyntaxKind.Identifier) { + return getTypeOfSymbol(getResolvedSymbol(expr)); + } + if (expr.kind === SyntaxKind.PropertyAccessExpression) { + const type = getDeclaredTypeOfReference((expr).expression); + return type && getTypeOfPropertyOfType(type, (expr).name.text); + } + return undefined; + } + + function isDiscriminantProperty(type: Type, name: string) { + if (type && type.flags & TypeFlags.Union) { + const prop = getPropertyOfType(type, name); + if (prop && prop.flags & SymbolFlags.SyntheticProperty) { + if ((prop).isDiscriminantProperty === undefined) { + (prop).isDiscriminantProperty = !(prop).hasCommonType && + isUnitUnionType(getTypeOfSymbol(prop)); + } + return (prop).isDiscriminantProperty; + } + } + return false; } function isOrContainsMatchingReference(source: Node, target: Node) { @@ -7901,7 +7947,7 @@ namespace ts { } if (flags & TypeFlags.TypeParameter) { const constraint = getConstraintOfTypeParameter(type); - return constraint ? getTypeFacts(constraint) : TypeFacts.All; + return getTypeFacts(constraint || emptyObjectType); } if (flags & TypeFlags.UnionOrIntersection) { return getTypeFactsOfTypes((type).types); @@ -7928,7 +7974,8 @@ namespace ts { } } } - return firstType ? types ? getUnionType(types) : firstType : neverType; + return types ? getUnionType(types) : + firstType ? firstType : neverType; } function getTypeWithDefault(type: Type, defaultExpression: Expression) { @@ -8084,6 +8131,25 @@ namespace ts { return source.flags & TypeFlags.Union ? !forEach((source).types, t => !contains(types, t)) : contains(types, source); } + function isTypeSubsetOf(source: Type, target: Type) { + return source === target || target.flags & TypeFlags.Union && isTypeSubsetOfUnion(source, target); + } + + function isTypeSubsetOfUnion(source: Type, target: UnionType) { + if (source.flags & TypeFlags.Union) { + for (const t of (source).types) { + if (!containsType(target.types, t)) { + return false; + } + } + return true; + } + if (source.flags & TypeFlags.EnumLiteral && target.flags & TypeFlags.Enum && (source).baseType === target) { + return true; + } + return containsType(target.types, source); + } + function filterType(type: Type, f: (t: Type) => boolean): Type { return type.flags & TypeFlags.Union ? getUnionType(filter((type).types, f)) : @@ -8222,7 +8288,7 @@ namespace ts { if (isMatchingReference(reference, expr)) { type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); } - else if (isMatchingPropertyAccess(expr)) { + else if (isMatchingReferenceDiscriminant(expr)) { type = narrowTypeByDiscriminant(type, expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd)); } return createFlowType(type, isIncomplete(flowType)); @@ -8230,6 +8296,7 @@ namespace ts { function getTypeAtFlowBranchLabel(flow: FlowLabel): FlowType { const antecedentTypes: Type[] = []; + let subtypeReduction = false; let seenIncomplete = false; for (const antecedent of flow.antecedents) { const flowType = getTypeAtFlowNode(antecedent); @@ -8244,11 +8311,17 @@ namespace ts { if (!contains(antecedentTypes, type)) { antecedentTypes.push(type); } + // If an antecedent type is not a subset of the declared type, we need to perform + // subtype reduction. This happens when a "foreign" type is injected into the control + // flow using the instanceof operator or a user defined type predicate. + if (!isTypeSubsetOf(type, declaredType)) { + subtypeReduction = true; + } if (isIncomplete(flowType)) { seenIncomplete = true; } } - return createFlowType(getUnionType(antecedentTypes), seenIncomplete); + return createFlowType(getUnionType(antecedentTypes, subtypeReduction), seenIncomplete); } function getTypeAtFlowLoopLabel(flow: FlowLabel): FlowType { @@ -8274,6 +8347,7 @@ namespace ts { // Add the flow loop junction and reference to the in-process stack and analyze // each antecedent code path. const antecedentTypes: Type[] = []; + let subtypeReduction = false; flowLoopNodes[flowLoopCount] = flow; flowLoopKeys[flowLoopCount] = key; flowLoopTypes[flowLoopCount] = antecedentTypes; @@ -8290,6 +8364,12 @@ namespace ts { if (!contains(antecedentTypes, type)) { antecedentTypes.push(type); } + // If an antecedent type is not a subset of the declared type, we need to perform + // subtype reduction. This happens when a "foreign" type is injected into the control + // flow using the instanceof operator or a user defined type predicate. + if (!isTypeSubsetOf(type, declaredType)) { + subtypeReduction = true; + } // If the type at a particular antecedent path is the declared type there is no // reason to process more antecedents since the only possible outcome is subtypes // that will be removed in the final union type anyway. @@ -8297,13 +8377,14 @@ namespace ts { break; } } - return cache[key] = getUnionType(antecedentTypes); + return cache[key] = getUnionType(antecedentTypes, subtypeReduction); } - function isMatchingPropertyAccess(expr: Expression) { + function isMatchingReferenceDiscriminant(expr: Expression) { return expr.kind === SyntaxKind.PropertyAccessExpression && + declaredType.flags & TypeFlags.Union && isMatchingReference(reference, (expr).expression) && - (declaredType.flags & TypeFlags.Union) !== 0; + isDiscriminantProperty(declaredType, (expr).name.text); } function narrowTypeByDiscriminant(type: Type, propAccess: PropertyAccessExpression, narrowType: (t: Type) => Type): Type { @@ -8317,10 +8398,10 @@ namespace ts { if (isMatchingReference(reference, expr)) { return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy); } - if (isMatchingPropertyAccess(expr)) { + if (isMatchingReferenceDiscriminant(expr)) { return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy)); } - if (rootContainsMatchingReference(reference, expr)) { + if (containsMatchingReferenceDiscriminant(reference, expr)) { return declaredType; } return type; @@ -8349,13 +8430,13 @@ namespace ts { if (isMatchingReference(reference, right)) { return narrowTypeByEquality(type, operator, left, assumeTrue); } - if (isMatchingPropertyAccess(left)) { + if (isMatchingReferenceDiscriminant(left)) { return narrowTypeByDiscriminant(type, left, t => narrowTypeByEquality(t, operator, right, assumeTrue)); } - if (isMatchingPropertyAccess(right)) { + if (isMatchingReferenceDiscriminant(right)) { return narrowTypeByDiscriminant(type, right, t => narrowTypeByEquality(t, operator, left, assumeTrue)); } - if (rootContainsMatchingReference(reference, left) || rootContainsMatchingReference(reference, right)) { + if (containsMatchingReferenceDiscriminant(reference, left) || containsMatchingReferenceDiscriminant(reference, right)) { return declaredType; } break; @@ -8494,9 +8575,7 @@ namespace ts { function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean) { if (!assumeTrue) { - return type.flags & TypeFlags.Union ? - getUnionType(filter((type).types, t => !isTypeSubtypeOf(t, candidate))) : - type; + return filterType(type, t => !isTypeSubtypeOf(t, candidate)); } // If the current type is a union type, remove all constituents that aren't assignable to // the candidate type. If one or more constituents remain, return a union of those. @@ -8506,13 +8585,16 @@ namespace ts { return getUnionType(assignableConstituents); } } - // If the candidate type is assignable to the target type, narrow to the candidate type. - // Otherwise, if the current type is assignable to the candidate, keep the current type. - // Otherwise, the types are completely unrelated, so narrow to the empty type. + // If the candidate type is a subtype of the target type, narrow to the candidate type. + // Otherwise, if the target type is assignable to the candidate type, keep the target type. + // Otherwise, if the candidate type is assignable to the target type, narrow to the candidate + // type. Otherwise, the types are completely unrelated, so narrow to an intersection of the + // two types. const targetType = type.flags & TypeFlags.TypeParameter ? getApparentType(type) : type; - return isTypeAssignableTo(candidate, targetType) ? candidate : + return isTypeSubtypeOf(candidate, targetType) ? candidate : isTypeAssignableTo(type, candidate) ? type : - getIntersectionType([type, candidate]); + isTypeAssignableTo(candidate, targetType) ? candidate : + getIntersectionType([type, candidate]); } function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { @@ -12855,6 +12937,9 @@ namespace ts { return checkDestructuringAssignment(element, type, contextualMapper); } else { + // We still need to check element expression here because we may need to set appropriate flag on the expression + // such as NodeCheckFlags.LexicalThis on "this"expression. + checkExpression(element); if (isTupleType(sourceType)) { error(element, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(sourceType), (sourceType).elementTypes.length, elements.length); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 28edede..a6e8604 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2160,6 +2160,8 @@ namespace ts { mapper?: TypeMapper; // Type mapper for instantiation alias referenced?: boolean; // True if alias symbol has been referenced as a value containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property + hasCommonType?: boolean; // True if constituents of synthetic property all have same type + isDiscriminantProperty?: boolean; // True if discriminant synthetic property resolvedExports?: SymbolTable; // Resolved exports of module exportsChecked?: boolean; // True if exports of external module have been checked isDeclarationWithCollidingName?: boolean; // True if symbol is block scoped redeclaration