Initial implementation of Union Types

This commit is contained in:
Anders Hejlsberg 2014-10-04 09:19:47 -07:00
parent 438aa899ea
commit e836fe1729
6 changed files with 407 additions and 209 deletions

View File

@ -130,6 +130,7 @@ module ts {
var globalRegExpType: ObjectType;
var tupleTypes: Map<TupleType> = {};
var unionTypes: Map<UnionType> = {};
var stringLiteralTypes: Map<StringLiteralType> = {};
var emitExtends = false;
@ -1136,6 +1137,9 @@ module ts {
else if (type.flags & TypeFlags.Tuple) {
writeTupleType(<TupleType>type);
}
else if (type.flags & TypeFlags.Union) {
writeUnionType(<UnionType>type);
}
else if (type.flags & TypeFlags.Anonymous) {
writeAnonymousType(<ObjectType>type, allowFunctionOrConstructorTypeLiteral);
}
@ -1153,18 +1157,21 @@ module ts {
}
}
function writeTypeList(types: Type[]) {
function writeTypeList(types: Type[], union?: boolean) {
for (var i = 0; i < types.length; i++) {
if (i > 0) {
writePunctuation(writer, SyntaxKind.CommaToken);
if (union) {
writeSpace(writer);
}
writePunctuation(writer, union ? SyntaxKind.BarToken : SyntaxKind.CommaToken);
writeSpace(writer);
}
writeType(types[i], /*allowFunctionOrConstructorTypeLiteral*/ true);
writeType(types[i], /*allowFunctionOrConstructorTypeLiteral*/ !union);
}
}
function writeTypeReference(type: TypeReference) {
if (type.target === globalArrayType && !(flags & TypeFormatFlags.WriteArrayAsGenericType)) {
if (type.target === globalArrayType && !(flags & TypeFormatFlags.WriteArrayAsGenericType) && !(type.typeArguments[0].flags & TypeFlags.Union)) {
// If we are writing array element type the arrow style signatures are not allowed as
// we need to surround it by curlies, e.g. { (): T; }[]; as () => T[] would mean something different
writeType(type.typeArguments[0], /*allowFunctionOrConstructorTypeLiteral*/ false);
@ -1185,6 +1192,10 @@ module ts {
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
function writeUnionType(type: UnionType) {
writeTypeList(type.types, /*union*/ true);
}
function writeAnonymousType(type: ObjectType, allowFunctionOrConstructorTypeLiteral: boolean) {
// Always use 'typeof T' for type of class, enum, and module objects
if (type.symbol && type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
@ -1730,6 +1741,15 @@ module ts {
return links.type;
}
function getTypeOfUnionProperty(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.type) {
var types = map(links.unionType.types, t => getTypeOfSymbol(getPropertyOfType(getApparentType(t), symbol.name) || undefinedSymbol));
links.type = getUnionType(types);
}
return links.type;
}
function getTypeOfSymbol(symbol: Symbol): Type {
if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) {
return getTypeOfVariableOrParameterOrProperty(symbol);
@ -1749,6 +1769,9 @@ module ts {
if (symbol.flags & SymbolFlags.Instantiated) {
return getTypeOfInstantiatedSymbol(symbol);
}
if (symbol.flags & SymbolFlags.UnionProperty) {
return getTypeOfUnionProperty(symbol);
}
return unknownType;
}
@ -2045,6 +2068,71 @@ module 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 (var i = 0; i < s.length; i++) {
if (!compareSignatures(s[i], t[i], false, isTypeIdenticalTo)) return false;
}
return true;
}
function getUnionSignatures(types: Type[], kind: SignatureKind): Signature[] {
var signatureLists = map(types, t => getSignaturesOfType(t, kind));
var baseSignatures = signatureLists[0];
for (var i = 0; i < baseSignatures.length; i++) {
if (baseSignatures[i].typeParameters) return emptyArray;
}
for (var i = 1; i < signatureLists.length; i++) {
if (!signatureListsIdentical(baseSignatures, signatureLists[i])) return emptyArray;
}
var result = map(baseSignatures, cloneSignature);
for (var i = 0; i < result.length; i++) {
var s = result[i];
s.resolvedReturnType = undefined;
s.unionSignatures = map(signatureLists, signatures => signatures[i]);
}
return result;
}
function getUnionIndexType(types: Type[], kind: IndexKind): Type {
var indexTypes: Type[] = [];
for (var i = 0; i < types.length; i++) {
var indexType = getIndexTypeOfType(types[i], kind);
if (!indexType) return undefined;
indexTypes.push(indexType);
}
return getUnionType(indexTypes);
}
function resolveUnionTypeMembers(type: UnionType) {
var types: Type[] = [];
forEach(type.types, t => {
var apparentType = getApparentType(t);
if (!contains(types, apparentType)) {
types.push(apparentType);
}
});
if (types.length <= 1) {
var res = types.length ? resolveObjectTypeMembers(types[0]) : emptyObjectType;
setObjectTypeMembers(type, res.members, res.callSignatures, res.constructSignatures, res.stringIndexType, res.numberIndexType);
return;
}
var members: SymbolTable = {};
forEach(getPropertiesOfType(types[0]), prop => {
for (var i = 1; i < types.length; i++) {
if (!getPropertyOfType(types[i], prop.name)) return;
}
var symbol = <TransientSymbol>createSymbol(SymbolFlags.UnionProperty | SymbolFlags.Transient, prop.name);
symbol.unionType = type;
members[prop.name] = symbol;
});
var callSignatures = getUnionSignatures(types, SignatureKind.Call);
var constructSignatures = getUnionSignatures(types, SignatureKind.Construct);
var stringIndexType = getUnionIndexType(types, IndexKind.String);
var numberIndexType = getUnionIndexType(types, IndexKind.Number);
setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function resolveAnonymousTypeMembers(type: ObjectType) {
var symbol = type.symbol;
if (symbol.flags & SymbolFlags.TypeLiteral) {
@ -2093,6 +2181,9 @@ module ts {
else if (type.flags & TypeFlags.Tuple) {
resolveTupleTypeMembers(<TupleType>type);
}
else if (type.flags & TypeFlags.Union) {
resolveUnionTypeMembers(<UnionType>type);
}
else {
resolveTypeReferenceMembers(<TypeReference>type);
}
@ -2253,6 +2344,9 @@ module ts {
if (signature.target) {
var type = instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper);
}
else if (signature.unionSignatures) {
var type = getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature));
}
else {
var type = getReturnTypeFromBody(<FunctionDeclaration>signature.declaration);
}
@ -2561,6 +2655,70 @@ module ts {
return links.resolvedType;
}
function addTypeToSortedSet(sortedSet: Type[], type: Type) {
if (type.flags & TypeFlags.Union) {
addTypesToSortedSet(sortedSet, (<UnionType>type).types);
}
else {
var i = 0;
var id = type.id;
while (i < sortedSet.length && sortedSet[i].id < id) i++;
if (i === sortedSet.length || sortedSet[i].id !== id) {
sortedSet.splice(i, 0, type);
}
}
}
function addTypesToSortedSet(sortedTypes: Type[], types: Type[]) {
for (var i = 0, len = types.length; i < len; i++) {
addTypeToSortedSet(sortedTypes, types[i]);
}
}
function isSubtypeOfAny(candidate: Type, types: Type[]): boolean {
for (var i = 0, len = types.length; i < len; i++) {
if (candidate !== types[i] && isTypeSubtypeOf(candidate, types[i])) return true;
}
return false;
}
function removeSubtypes(types: Type[]) {
var i = types.length;
while (i > 0) {
i--;
if (isSubtypeOfAny(types[i], types)) {
types.splice(i, 1);
}
}
}
function getUnionType(types: Type[]): Type {
if (types.length === 0) {
return emptyObjectType;
}
var sortedTypes: Type[] = [];
addTypesToSortedSet(sortedTypes, types);
removeSubtypes(sortedTypes);
if (sortedTypes.length === 1) {
return sortedTypes[0];
}
var id = getTypeListId(sortedTypes);
var type = unionTypes[id];
if (!type) {
type = unionTypes[id] = <UnionType>createObjectType(TypeFlags.Union);
type.types = sortedTypes;
}
return type;
}
function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
var links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode));
}
return links.resolvedType;
}
function getTypeFromTypeLiteralNode(node: TypeLiteralNode): Type {
var links = getNodeLinks(node);
if (!links.resolvedType) {
@ -2607,6 +2765,8 @@ module ts {
return getTypeFromArrayTypeNode(<ArrayTypeNode>node);
case SyntaxKind.TupleType:
return getTypeFromTupleTypeNode(<TupleTypeNode>node);
case SyntaxKind.UnionType:
return getTypeFromUnionTypeNode(<UnionTypeNode>node);
case SyntaxKind.TypeLiteral:
return getTypeFromTypeLiteralNode(<TypeLiteralNode>node);
// This function assumes that an identifier or qualified name is a type expression
@ -2771,6 +2931,9 @@ module ts {
if (type.flags & TypeFlags.Tuple) {
return createTupleType(instantiateList((<TupleType>type).elementTypes, mapper, instantiateType));
}
if (type.flags & TypeFlags.Union) {
return getUnionType(instantiateList((<UnionType>type).types, mapper, instantiateType));
}
}
return type;
}
@ -2932,7 +3095,7 @@ module ts {
errorInfo = chainDiagnosticMessages(errorInfo, message, arg0, arg1, arg2);
}
function isRelatedTo(source: Type, target: Type, reportErrors: boolean): boolean {
function isRelatedTo(source: Type, target: Type, reportErrors?: boolean): boolean {
return isRelatedToWithCustomErrors(source, target, reportErrors, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
}
@ -2953,8 +3116,17 @@ module ts {
if (source === numberType && target.flags & TypeFlags.Enum) return true;
}
}
if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter) {
if (source.flags & TypeFlags.Union) {
if (unionTypeRelatedToType(<UnionType>source, target, reportErrors)) {
return true;
}
}
else if (target.flags & TypeFlags.Union) {
if (typeRelatedToUnionType(source, <UnionType>target, reportErrors)) {
return true;
}
}
else if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter) {
if (typeParameterRelatedTo(<TypeParameter>source, <TypeParameter>target, reportErrors)) {
return true;
}
@ -2971,7 +3143,7 @@ module ts {
// Report structural errors only if we haven't reported any errors yet
var reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo;
// identity relation does not use apparent type
var sourceOrApparentType = relation === identityRelation ? source : getApparentType(source);
var sourceOrApparentType = relation === identityRelation ? source : getApparentType(source);
if (sourceOrApparentType.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType &&
objectTypeRelatedTo(sourceOrApparentType, <ObjectType>target, reportStructuralErrors)) {
errorInfo = saveErrorInfo;
@ -2992,6 +3164,26 @@ module ts {
return false;
}
function typeRelatedToUnionType(source: Type, target: UnionType, reportErrors: boolean): boolean {
var targetTypes = target.types;
for (var i = 0, len = targetTypes.length; i < len; i++) {
if (isRelatedTo(source, targetTypes[i], reportErrors && i === len - 1)) {
return true;
}
}
return false;
}
function unionTypeRelatedToType(source: UnionType, target: Type, reportErrors: boolean): boolean {
var sourceTypes = source.types;
for (var i = 0, len = sourceTypes.length; i < len; i++) {
if (!isRelatedTo(sourceTypes[i], target, reportErrors)) {
return false;
}
}
return true;
}
function typesRelatedTo(sources: Type[], targets: Type[], reportErrors: boolean): boolean {
for (var i = 0, len = sources.length; i < len; i++) {
if (!isRelatedTo(sources[i], targets[i], reportErrors)) return false;
@ -3093,34 +3285,12 @@ module ts {
function propertiesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
if (relation === identityRelation) {
return propertiesAreIdenticalTo(source, target, reportErrors);
return propertiesIdenticalTo(source, target, reportErrors);
}
else {
return propertiesAreSubtypeOrAssignableTo(<ApparentType>source, target, reportErrors);
}
}
function propertiesAreIdenticalTo(source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
var sourceProperties = getPropertiesOfType(source);
var targetProperties = getPropertiesOfType(target);
if (sourceProperties.length !== targetProperties.length) {
return false;
}
for (var i = 0, len = sourceProperties.length; i < len; ++i) {
var sourceProp = sourceProperties[i];
var targetProp = getPropertyOfType(target, sourceProp.name);
if (!targetProp || !isPropertyIdenticalToRecursive(sourceProp, targetProp, reportErrors, isRelatedTo)) {
return false;
}
}
return true;
}
function propertiesAreSubtypeOrAssignableTo(source: ApparentType, target: ObjectType, reportErrors: boolean): boolean {
var properties = getPropertiesOfType(target);
for (var i = 0; i < properties.length; i++) {
var targetProp = properties[i];
var sourceProp = getPropertyOfApparentType(source, targetProp.name);
var sourceProp = getPropertyOfApparentType(<ApparentType>source, targetProp.name);
if (sourceProp !== targetProp) {
if (!sourceProp) {
if (!isOptionalProperty(targetProp)) {
@ -3193,80 +3363,26 @@ module ts {
return true;
}
function signaturesRelatedTo(source: ObjectType, target: ObjectType, kind: SignatureKind, reportErrors: boolean): boolean {
if (relation === identityRelation) {
return areSignaturesIdenticalTo(source, target, kind, reportErrors);
}
else {
return areSignaturesSubtypeOrAssignableTo(source, target, kind, reportErrors);
}
}
function areSignaturesIdenticalTo(source: ObjectType, target: ObjectType, kind: SignatureKind, reportErrors: boolean): boolean {
var sourceSignatures = getSignaturesOfType(source, kind);
var targetSignatures = getSignaturesOfType(target, kind);
if (sourceSignatures.length !== targetSignatures.length) {
function propertiesIdenticalTo(source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
var sourceProperties = getPropertiesOfType(source);
var targetProperties = getPropertiesOfType(target);
if (sourceProperties.length !== targetProperties.length) {
return false;
}
for (var i = 0, len = sourceSignatures.length; i < len; ++i) {
if (!isSignatureIdenticalTo(sourceSignatures[i], targetSignatures[i], reportErrors)) {
for (var i = 0, len = sourceProperties.length; i < len; ++i) {
var sourceProp = sourceProperties[i];
var targetProp = getPropertyOfType(target, sourceProp.name);
if (!targetProp || !isPropertyIdenticalToRecursive(sourceProp, targetProp, reportErrors, isRelatedTo)) {
return false;
}
}
return true;
}
function isSignatureIdenticalTo(source: Signature, target: Signature, reportErrors: boolean): boolean {
if (source === target) {
return true;
function signaturesRelatedTo(source: ObjectType, target: ObjectType, kind: SignatureKind, reportErrors: boolean): boolean {
if (relation === identityRelation) {
return signaturesIdenticalTo(source, target, kind, reportErrors);
}
if (source.hasRestParameter !== target.hasRestParameter) {
return false;
}
if (source.parameters.length !== target.parameters.length) {
return false;
}
if (source.minArgumentCount !== target.minArgumentCount) {
return false;
}
if (source.typeParameters && target.typeParameters) {
if (source.typeParameters.length !== target.typeParameters.length) {
return false;
}
for (var i = 0, len = source.typeParameters.length; i < len; ++i) {
if (!isRelatedTo(source.typeParameters[i], target.typeParameters[i], reportErrors)) {
return false;
}
}
}
else if (source.typeParameters || source.typeParameters) {
return false;
}
// Spec 1.0 Section 3.8.3 & 3.8.4:
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
source = getErasedSignature(source);
target = getErasedSignature(target);
for (var i = 0, len = source.parameters.length; i < len; i++) {
var s = source.hasRestParameter && i === len - 1 ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]);
var t = target.hasRestParameter && i === len - 1 ? getRestTypeOfSignature(target) : getTypeOfSymbol(target.parameters[i]);
if (!isRelatedTo(s, t, reportErrors)) {
return false;
}
}
var t = getReturnTypeOfSignature(target);
var s = getReturnTypeOfSignature(source);
return isRelatedTo(s, t, reportErrors);
}
function areSignaturesSubtypeOrAssignableTo(source: ObjectType, target: ObjectType, kind: SignatureKind, reportErrors: boolean): boolean {
if (target === anyFunctionType || source === anyFunctionType) return true;
var sourceSignatures = getSignaturesOfType(source, kind);
var targetSignatures = getSignaturesOfType(target, kind);
@ -3278,7 +3394,7 @@ module ts {
for (var j = 0; j < sourceSignatures.length; j++) {
var s = sourceSignatures[j];
if (!s.hasStringLiterals || source.flags & TypeFlags.FromSignature) {
if (isSignatureSubtypeOrAssignableTo(s, t, localErrors)) {
if (signatureRelatedTo(s, t, localErrors)) {
errorInfo = saveErrorInfo;
continue outer;
}
@ -3292,15 +3408,13 @@ module ts {
return true;
}
function isSignatureSubtypeOrAssignableTo(source: Signature, target: Signature, reportErrors: boolean): boolean {
function signatureRelatedTo(source: Signature, target: Signature, reportErrors: boolean): boolean {
if (source === target) {
return true;
}
if (!target.hasRestParameter && source.minArgumentCount > target.parameters.length) {
return false;
}
var sourceMax = source.parameters.length;
var targetMax = target.parameters.length;
var checkCount: number;
@ -3346,71 +3460,117 @@ module ts {
return isRelatedTo(s, t, reportErrors);
}
function signaturesIdenticalTo(source: ObjectType, target: ObjectType, kind: SignatureKind, reportErrors: boolean): boolean {
var sourceSignatures = getSignaturesOfType(source, kind);
var targetSignatures = getSignaturesOfType(target, kind);
if (sourceSignatures.length !== targetSignatures.length) {
return false;
}
for (var i = 0, len = sourceSignatures.length; i < len; ++i) {
if (!compareSignatures(sourceSignatures[i], targetSignatures[i], /*returnTypes*/ true, isRelatedTo)) {
return false;
}
}
return true;
}
function stringIndexTypesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
if (relation === identityRelation) {
return areIndexTypesIdenticalTo(IndexKind.String, source, target, reportErrors);
return indexTypesIdenticalTo(IndexKind.String, source, target, reportErrors);
}
else {
var targetType = getIndexTypeOfType(target, IndexKind.String);
if (targetType) {
var sourceType = getIndexTypeOfType(source, IndexKind.String);
if (!sourceType) {
if (reportErrors) {
reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source));
}
return false;
}
if (!isRelatedTo(sourceType, targetType, reportErrors)) {
if (reportErrors) {
reportError(Diagnostics.Index_signatures_are_incompatible_Colon);
}
return false;
var targetType = getIndexTypeOfType(target, IndexKind.String);
if (targetType) {
var sourceType = getIndexTypeOfType(source, IndexKind.String);
if (!sourceType) {
if (reportErrors) {
reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source));
}
return false;
}
if (!isRelatedTo(sourceType, targetType, reportErrors)) {
if (reportErrors) {
reportError(Diagnostics.Index_signatures_are_incompatible_Colon);
}
return false;
}
return true;
}
return true;
}
function numberIndexTypesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
if (relation === identityRelation) {
return areIndexTypesIdenticalTo(IndexKind.Number, source, target, reportErrors);
return indexTypesIdenticalTo(IndexKind.Number, source, target, reportErrors);
}
else {
var targetType = getIndexTypeOfType(target, IndexKind.Number);
if (targetType) {
var sourceStringType = getIndexTypeOfType(source, IndexKind.String);
var sourceNumberType = getIndexTypeOfType(source, IndexKind.Number);
if (!(sourceStringType || sourceNumberType)) {
if (reportErrors) {
reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source));
}
return false;
}
if (sourceStringType && sourceNumberType) {
// If we know for sure we're testing both string and numeric index types then only report errors from the second one
var compatible = isRelatedTo(sourceStringType, targetType, false) || isRelatedTo(sourceNumberType, targetType, reportErrors);
}
else {
var compatible = isRelatedTo(sourceStringType || sourceNumberType, targetType, reportErrors);
}
if (!compatible) {
if (reportErrors) {
reportError(Diagnostics.Index_signatures_are_incompatible_Colon);
}
return false;
var targetType = getIndexTypeOfType(target, IndexKind.Number);
if (targetType) {
var sourceStringType = getIndexTypeOfType(source, IndexKind.String);
var sourceNumberType = getIndexTypeOfType(source, IndexKind.Number);
if (!(sourceStringType || sourceNumberType)) {
if (reportErrors) {
reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source));
}
return false;
}
if (sourceStringType && sourceNumberType) {
// If we know for sure we're testing both string and numeric index types then only report errors from the second one
var compatible = isRelatedTo(sourceStringType, targetType, false) || isRelatedTo(sourceNumberType, targetType, reportErrors);
}
else {
var compatible = isRelatedTo(sourceStringType || sourceNumberType, targetType, reportErrors);
}
if (!compatible) {
if (reportErrors) {
reportError(Diagnostics.Index_signatures_are_incompatible_Colon);
}
return false;
}
return true;
}
return true;
}
function areIndexTypesIdenticalTo(indexKind: IndexKind, source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
function indexTypesIdenticalTo(indexKind: IndexKind, source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
var targetType = getIndexTypeOfType(target, indexKind);
var sourceType = getIndexTypeOfType(source, indexKind);
return (!sourceType && !targetType) || (sourceType && targetType && isRelatedTo(sourceType, targetType, reportErrors));
}
}
function compareSignatures(source: Signature, target: Signature, returnTypes: boolean, compareTypes: (s: Type, t: Type) => boolean): boolean {
if (source === target) {
return true;
}
if (source.parameters.length !== target.parameters.length ||
source.minArgumentCount !== target.minArgumentCount ||
source.hasRestParameter !== target.hasRestParameter) {
return false;
}
if (source.typeParameters && target.typeParameters) {
if (source.typeParameters.length !== target.typeParameters.length) {
return false;
}
for (var i = 0, len = source.typeParameters.length; i < len; ++i) {
if (!compareTypes(source.typeParameters[i], target.typeParameters[i])) {
return false;
}
}
}
else if (source.typeParameters || source.typeParameters) {
return false;
}
// Spec 1.0 Section 3.8.3 & 3.8.4:
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
source = getErasedSignature(source);
target = getErasedSignature(target);
for (var i = 0, len = source.parameters.length; i < len; i++) {
var s = source.hasRestParameter && i === len - 1 ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]);
var t = target.hasRestParameter && i === len - 1 ? getRestTypeOfSignature(target) : getTypeOfSymbol(target.parameters[i]);
if (!compareTypes(s, t)) {
return false;
}
}
return !returnTypes || compareTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target));
}
function isSupertypeOfEach(candidate: Type, types: Type[]): boolean {
for (var i = 0, len = types.length; i < len; i++) {
if (candidate !== types[i] && !isTypeSubtypeOf(types[i], candidate)) return false;
@ -3418,11 +3578,13 @@ module ts {
return true;
}
function getBestCommonType(types: Type[], contextualType?: Type, candidatesOnly?: boolean): Type {
if (contextualType && isSupertypeOfEach(contextualType, types)) return contextualType;
return forEach(types, t => isSupertypeOfEach(t, types) ? t : undefined) || (candidatesOnly ? undefined : emptyObjectType);
function getCommonSupertype(types: Type[]): Type {
return forEach(types, t => isSupertypeOfEach(t, types) ? t : undefined);
}
function getBestCommonType(types: Type[], contextualType?: Type): Type {
return contextualType && isSupertypeOfEach(contextualType, types) ? contextualType : getUnionType(types); }
function isTypeOfObjectLiteral(type: Type): boolean {
return (type.flags & TypeFlags.Anonymous) && type.symbol && (type.symbol.flags & SymbolFlags.ObjectLiteral) ? true : false;
}
@ -3439,10 +3601,13 @@ module ts {
}
/* If we are widening on a literal, then we may need to the 'node' parameter for reporting purposes */
function getWidenedType(type: Type, supressNoImplicitAnyErrors?: boolean): Type {
function getWidenedType(type: Type, suppressNoImplicitAnyErrors?: boolean): Type {
if (type.flags & (TypeFlags.Undefined | TypeFlags.Null)) {
return anyType;
}
if (type.flags & TypeFlags.Union) {
return getWidenedTypeOfUnion(type);
}
if (isTypeOfObjectLiteral(type)) {
return getWidenedTypeOfObjectLiteral(type);
}
@ -3451,6 +3616,10 @@ module ts {
}
return type;
function getWidenedTypeOfUnion(type: Type): Type {
return getUnionType(map((<UnionType>type).types, t => getWidenedType(t, suppressNoImplicitAnyErrors)));
}
function getWidenedTypeOfObjectLiteral(type: Type): Type {
var properties = getPropertiesOfType(type);
if (properties.length) {
@ -3461,8 +3630,7 @@ module ts {
var widenedType = getWidenedType(propType);
if (propType !== widenedType) {
propTypeWasWidened = true;
if (!supressNoImplicitAnyErrors && program.getCompilerOptions().noImplicitAny && getInnermostTypeOfNestedArrayTypes(widenedType) === anyType) {
if (!suppressNoImplicitAnyErrors && compilerOptions.noImplicitAny && getInnermostTypeOfNestedArrayTypes(widenedType) === anyType) {
error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, p.name, typeToString(widenedType));
}
}
@ -3492,10 +3660,8 @@ module ts {
function getWidenedTypeOfArrayLiteral(type: Type): Type {
var elementType = (<TypeReference>type).typeArguments[0];
var widenedType = getWidenedType(elementType, supressNoImplicitAnyErrors);
var widenedType = getWidenedType(elementType, suppressNoImplicitAnyErrors);
type = elementType !== widenedType ? createArrayType(widenedType) : type;
return type;
}
}
@ -3646,9 +3812,19 @@ module ts {
function getInferredType(context: InferenceContext, index: number): Type {
var result = context.inferredTypes[index];
if (!result) {
var commonType = getWidenedType(getBestCommonType(context.inferences[index]));
var inferences = context.inferences[index];
if (inferences.length) {
// Find type that is supertype of all others
var supertype = getCommonSupertype(inferences);
// Infer widened supertype, or the undefined type for no common supertype
var inferredType = supertype ? getWidenedType(supertype) : undefinedType;
}
else {
// Infer the empty object type when no inferences were made
inferredType = emptyObjectType;
}
var constraint = getConstraintOfTypeParameter(context.typeParameters[index]);
var result = constraint && !isTypeAssignableTo(commonType, constraint) ? constraint : commonType;
var result = constraint && !isTypeAssignableTo(inferredType, constraint) ? constraint : inferredType;
context.inferredTypes[index] = result;
}
return result;
@ -4083,10 +4259,7 @@ module ts {
return createTupleType(elementTypes);
}
var contextualElementType = contextualType && !isInferentialContext(contextualMapper) ? getIndexTypeOfType(contextualType, IndexKind.Number) : undefined;
var elementType = getBestCommonType(uniqueElements(elementTypes), contextualElementType, true);
if (!elementType) {
elementType = elements.length ? emptyObjectType : undefinedType;
}
var elementType = elements.length || contextualElementType ? getBestCommonType(deduplicate(elementTypes), contextualElementType) : undefinedType;
return createArrayType(elementType);
}
@ -4380,7 +4553,9 @@ module ts {
}
}
}
return getInferredTypes(context);
var inferredTypes = getInferredTypes(context);
// Inference has failed if the undefined type is in list of inferences
return contains(inferredTypes, undefinedType) ? undefined : inferredTypes;
}
function checkTypeArguments(signature: Signature, typeArguments: TypeNode[]): Type[] {
@ -4405,7 +4580,6 @@ module ts {
if (arg.kind === SyntaxKind.OmittedExpression) {
continue;
}
var paramType = getTypeAtPosition(signature, i);
// String literals get string literal types unless we're reporting errors
var argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors ?
@ -4441,26 +4615,30 @@ module ts {
}
}
var relation = candidates.length === 1 ? assignableRelation : subtypeRelation;
var lastCandidate: Signature;
while (true) {
for (var i = 0; i < candidates.length; i++) {
if (!signatureHasCorrectArity(node, candidates[i])) {
continue;
}
while (true) {
var candidateWithCorrectArity = candidates[i];
if (candidateWithCorrectArity.typeParameters) {
var candidate = candidates[i];
if (candidate.typeParameters) {
var typeArguments = node.typeArguments ?
checkTypeArguments(candidateWithCorrectArity, node.typeArguments) :
inferTypeArguments(candidateWithCorrectArity, args, excludeArgument);
candidateWithCorrectArity = getSignatureInstantiation(candidateWithCorrectArity, typeArguments);
checkTypeArguments(candidate, node.typeArguments) :
inferTypeArguments(candidate, args, excludeArgument);
if (!typeArguments) {
break;
}
candidate = getSignatureInstantiation(candidate, typeArguments);
}
if (!checkApplicableSignature(node, candidateWithCorrectArity, relation, excludeArgument, /*reportErrors*/ false)) {
lastCandidate = candidate;
if (!checkApplicableSignature(node, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
break;
}
var index = excludeArgument ? indexOf(excludeArgument, true) : -1;
if (index < 0) {
return candidateWithCorrectArity;
return candidate;
}
excludeArgument[index] = false;
}
@ -4475,8 +4653,8 @@ module ts {
// no arguments excluded from assignability checks.
// If candidate is undefined, it means that no candidates had a suitable arity. In that case,
// skip the checkApplicableSignature check.
if (candidateWithCorrectArity) {
checkApplicableSignature(node, candidateWithCorrectArity, relation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
if (lastCandidate) {
checkApplicableSignature(node, lastCandidate, relation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
}
else {
error(node, Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target);
@ -4563,7 +4741,9 @@ module ts {
// but is a subtype of the Function interface, the call is an untyped function call. In an
// untyped function call no TypeArgs are permitted, Args can be any argument list, no contextual
// types are provided for the argument expressions, and the result is always of type Any.
if ((funcType === anyType) || (!callSignatures.length && !constructSignatures.length && isTypeAssignableTo(funcType, globalFunctionType))) {
// We exclude union types because we may have a union of function types that happen to have
// no common signatures.
if (funcType === anyType || (!callSignatures.length && !constructSignatures.length && !(funcType.flags & TypeFlags.Union) && isTypeAssignableTo(funcType, globalFunctionType))) {
if (node.typeArguments) {
error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments);
}
@ -4723,7 +4903,7 @@ module ts {
// Try to return the best common type if we have any return expressions.
if (types.length > 0) {
var commonType = getBestCommonType(types, /*contextualType:*/ undefined, /*candidatesOnly:*/ true);
var commonType = getCommonSupertype(types);
if (!commonType) {
error(func, Diagnostics.No_best_common_type_exists_among_return_expressions);
@ -5118,16 +5298,7 @@ module ts {
var type1 = checkExpression(node.whenTrue, contextualMapper);
var type2 = checkExpression(node.whenFalse, contextualMapper);
var contextualType = isInferentialContext(contextualMapper) ? undefined : getContextualType(node);
var resultType = getBestCommonType([type1, type2], contextualType, true);
if (!resultType) {
if (contextualType) {
error(node, Diagnostics.No_best_common_type_exists_between_0_1_and_2, typeToString(contextualType), typeToString(type1), typeToString(type2));
}
else {
error(node, Diagnostics.No_best_common_type_exists_between_0_and_1, typeToString(type1), typeToString(type2));
}
resultType = emptyObjectType;
}
var resultType = getBestCommonType([type1, type2], contextualType);
return resultType;
}
@ -5520,6 +5691,10 @@ module ts {
forEach(node.elementTypes, checkSourceElement);
}
function checkUnionType(node: UnionTypeNode) {
forEach(node.types, checkSourceElement);
}
function isPrivateWithinAmbient(node: Node): boolean {
return (node.flags & NodeFlags.Private) && isInAmbientContext(node);
}
@ -6782,6 +6957,8 @@ module ts {
return checkArrayType(<ArrayTypeNode>node);
case SyntaxKind.TupleType:
return checkTupleType(<TupleTypeNode>node);
case SyntaxKind.TupleType:
return checkUnionType(<UnionTypeNode>node);
case SyntaxKind.FunctionDeclaration:
return checkFunctionDeclaration(<FunctionDeclaration>node);
case SyntaxKind.Block:

View File

@ -82,7 +82,7 @@ module ts {
return array1.concat(array2);
}
export function uniqueElements<T>(array: T[]): T[] {
export function deduplicate<T>(array: T[]): T[] {
if (array) {
var result: T[] = [];
for (var i = 0, len = array.length; i < len; i++) {

View File

@ -179,8 +179,6 @@ module ts {
The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type: { code: 2363, category: DiagnosticCategory.Error, key: "The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type." },
Invalid_left_hand_side_of_assignment_expression: { code: 2364, category: DiagnosticCategory.Error, key: "Invalid left-hand side of assignment expression." },
Operator_0_cannot_be_applied_to_types_1_and_2: { code: 2365, category: DiagnosticCategory.Error, key: "Operator '{0}' cannot be applied to types '{1}' and '{2}'." },
No_best_common_type_exists_between_0_1_and_2: { code: 2366, category: DiagnosticCategory.Error, key: "No best common type exists between '{0}', '{1}', and '{2}'." },
No_best_common_type_exists_between_0_and_1: { code: 2367, category: DiagnosticCategory.Error, key: "No best common type exists between '{0}' and '{1}'." },
Type_parameter_name_cannot_be_0: { code: 2368, category: DiagnosticCategory.Error, key: "Type parameter name cannot be '{0}'" },
A_parameter_property_is_only_allowed_in_a_constructor_implementation: { code: 2369, category: DiagnosticCategory.Error, key: "A parameter property is only allowed in a constructor implementation." },
A_rest_parameter_must_be_of_an_array_type: { code: 2370, category: DiagnosticCategory.Error, key: "A rest parameter must be of an array type." },

View File

@ -708,14 +708,6 @@
"category": "Error",
"code": 2365
},
"No best common type exists between '{0}', '{1}', and '{2}'.": {
"category": "Error",
"code": 2366
},
"No best common type exists between '{0}' and '{1}'.": {
"category": "Error",
"code": 2367
},
"Type parameter name cannot be '{0}'": {
"category": "Error",
"code": 2368

View File

@ -232,6 +232,8 @@ module ts {
return child((<ArrayTypeNode>node).elementType);
case SyntaxKind.TupleType:
return children((<TupleTypeNode>node).elementTypes);
case SyntaxKind.UnionType:
return children((<UnionTypeNode>node).types);
case SyntaxKind.ArrayLiteral:
return children((<ArrayLiteral>node).elements);
case SyntaxKind.ObjectLiteral:
@ -1728,9 +1730,9 @@ module ts {
}
}
function parseType(): TypeNode {
function parseNonUnionType(): TypeNode {
var type = parseNonArrayType();
while (type && !scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) {
while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) {
parseExpected(SyntaxKind.CloseBracketToken);
var node = <ArrayTypeNode>createNode(SyntaxKind.ArrayType, type.pos);
node.elementType = type;
@ -1739,6 +1741,22 @@ module ts {
return type;
}
function parseType(): TypeNode {
var type = parseNonUnionType();
if (token === SyntaxKind.BarToken) {
var types = <NodeArray<TypeNode>>[type];
types.pos = type.pos;
while (parseOptional(SyntaxKind.BarToken)) {
types.push(parseNonUnionType());
}
types.end = getNodeEnd();
var node = <UnionTypeNode>createNode(SyntaxKind.UnionType, type.pos);
node.types = types;
type = finishNode(node);
}
return type;
}
function parseTypeAnnotation(): TypeNode {
return parseOptional(SyntaxKind.ColonToken) ? parseType() : undefined;
}

View File

@ -154,6 +154,7 @@ module ts {
TypeLiteral,
ArrayType,
TupleType,
UnionType,
// Expression
ArrayLiteral,
ObjectLiteral,
@ -224,7 +225,7 @@ module ts {
FirstFutureReservedWord = ImplementsKeyword,
LastFutureReservedWord = YieldKeyword,
FirstTypeNode = TypeReference,
LastTypeNode = TupleType,
LastTypeNode = UnionType,
FirstPunctuation = OpenBraceToken,
LastPunctuation = CaretEqualsToken,
FirstToken = EndOfFileToken,
@ -337,6 +338,10 @@ module ts {
elementTypes: NodeArray<TypeNode>;
}
export interface UnionTypeNode extends TypeNode {
types: NodeArray<TypeNode>;
}
export interface StringLiteralTypeNode extends TypeNode {
text: string;
}
@ -728,19 +733,20 @@ module ts {
ConstructSignature = 0x00010000, // Construct signature
IndexSignature = 0x00020000, // Index signature
TypeParameter = 0x00040000, // Type parameter
UnionProperty = 0x00080000, // Property in union type
// Export markers (see comment in declareModuleMember in binder)
ExportValue = 0x00080000, // Exported value marker
ExportType = 0x00100000, // Exported type marker
ExportNamespace = 0x00200000, // Exported namespace marker
ExportValue = 0x00100000, // Exported value marker
ExportType = 0x00200000, // Exported type marker
ExportNamespace = 0x00400000, // Exported namespace marker
Import = 0x00400000, // Import
Instantiated = 0x00800000, // Instantiated symbol
Merged = 0x01000000, // Merged symbol (created during program binding)
Transient = 0x02000000, // Transient symbol (created during type check)
Prototype = 0x04000000, // Symbol for the prototype property (without source code representation)
Import = 0x00800000, // Import
Instantiated = 0x01000000, // Instantiated symbol
Merged = 0x02000000, // Merged symbol (created during program binding)
Transient = 0x04000000, // Transient symbol (created during type check)
Prototype = 0x08000000, // Prototype property (no source representation)
Value = Variable | Property | EnumMember | Function | Class | Enum | ValueModule | Method | GetAccessor | SetAccessor,
Value = Variable | Property | EnumMember | Function | Class | Enum | ValueModule | Method | GetAccessor | SetAccessor | UnionProperty,
Type = Class | Interface | Enum | TypeLiteral | ObjectLiteral | TypeParameter,
Namespace = ValueModule | NamespaceModule,
Module = ValueModule | NamespaceModule,
@ -798,6 +804,7 @@ module ts {
mapper?: TypeMapper; // Type mapper for instantiation alias
referenced?: boolean; // True if alias symbol has been referenced as a value
exportAssignSymbol?: Symbol; // Symbol exported from external module
unionType?: UnionType; // Containing union type for union property
}
export interface TransientSymbol extends Symbol, SymbolLinks { }
@ -845,13 +852,14 @@ module ts {
Interface = 0x00000800, // Interface
Reference = 0x00001000, // Generic type reference
Tuple = 0x00002000, // Tuple
Anonymous = 0x00004000, // Anonymous
FromSignature = 0x00008000, // Created for signature assignment check
Union = 0x00004000, // Union
Anonymous = 0x00008000, // Anonymous
FromSignature = 0x00010000, // Created for signature assignment check
Intrinsic = Any | String | Number | Boolean | Void | Undefined | Null,
StringLike = String | StringLiteral,
NumberLike = Number | Enum,
ObjectType = Class | Interface | Reference | Tuple | Anonymous
ObjectType = Class | Interface | Reference | Tuple | Union | Anonymous
}
// Properties common to all types
@ -909,6 +917,10 @@ module ts {
baseArrayType: TypeReference; // Array<T> where T is best common type of element types
}
export interface UnionType extends ObjectType {
types: Type[]; // Constituent types
}
// Resolved object type
export interface ResolvedObjectType extends ObjectType {
members: SymbolTable; // Properties by name
@ -941,6 +953,7 @@ module ts {
hasStringLiterals: boolean; // True if specialized
target?: Signature; // Instantiation target
mapper?: TypeMapper; // Instantiation mapper
unionSignatures?: Signature[]; // Underlying signatures of a union signature
erasedSignatureCache?: Signature; // Erased version of signature (deferred)
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
}