diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 01256d0f63817..b99d6d27d20fb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -102,6 +102,7 @@ module ts { var globalBooleanType: ObjectType; var globalRegExpType: ObjectType; + var tupleTypes: Map = {}; var stringLiteralTypes: Map = {}; var emitExtends = false; @@ -649,15 +650,14 @@ module ts { } function isOptionalProperty(propertySymbol: Symbol): boolean { - if (propertySymbol.flags & SymbolFlags.Prototype) { - return false; - } // class C { // constructor(public x?) { } // } // // x is an optional parameter, but it is a required property. - return (propertySymbol.valueDeclaration.flags & NodeFlags.QuestionMark) && propertySymbol.valueDeclaration.kind !== SyntaxKind.Parameter; + return propertySymbol.valueDeclaration && + propertySymbol.valueDeclaration.flags & NodeFlags.QuestionMark && + propertySymbol.valueDeclaration.kind !== SyntaxKind.Parameter; } function forEachSymbolTableInScope(enclosingDeclaration: Node, callback: (symbolTable: SymbolTable) => T): T { @@ -995,6 +995,9 @@ module ts { else if (type.flags & (TypeFlags.Class | TypeFlags.Interface | TypeFlags.Enum | TypeFlags.TypeParameter)) { writer.writeSymbol(type.symbol, enclosingDeclaration, SymbolFlags.Type); } + else if (type.flags & TypeFlags.Tuple) { + writeTupleType(type); + } else if (type.flags & TypeFlags.Anonymous) { writeAnonymousType(type, allowFunctionOrConstructorTypeLiteral); } @@ -1007,6 +1010,15 @@ module ts { } } + function writeTypeList(types: Type[]) { + for (var i = 0; i < types.length; i++) { + if (i > 0) { + writer.write(", "); + } + writeType(types[i], /*allowFunctionOrConstructorTypeLiteral*/ true); + } + } + function writeTypeReference(type: TypeReference) { if (type.target === globalArrayType && !(flags & TypeFormatFlags.WriteArrayAsGenericType)) { // If we are writing array element type the arrow style signatures are not allowed as @@ -1017,16 +1029,17 @@ module ts { else { writer.writeSymbol(type.target.symbol, enclosingDeclaration, SymbolFlags.Type); writer.write("<"); - for (var i = 0; i < type.typeArguments.length; i++) { - if (i > 0) { - writer.write(", "); - } - writeType(type.typeArguments[i], /*allowFunctionOrConstructorTypeLiteral*/ true); - } + writeTypeList(type.typeArguments); writer.write(">"); } } + function writeTupleType(type: TupleType) { + writer.write("["); + writeTypeList(type.elementTypes); + writer.write("]"); + } + 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)) { @@ -1832,6 +1845,23 @@ module ts { return [createSignature(undefined, classType.typeParameters, emptyArray, classType, 0, false, false)]; } + function createTupleTypeMemberSymbols(memberTypes: Type[]): SymbolTable { + var members: SymbolTable = {}; + for (var i = 0; i < memberTypes.length; i++) { + var symbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "" + i); + symbol.type = memberTypes[i]; + members[i] = symbol; + } + return members; + } + + function resolveTupleTypeMembers(type: TupleType) { + var arrayType = resolveObjectTypeMembers(createArrayType(getBestCommonType(type.elementTypes))); + var members = createTupleTypeMemberSymbols(type.elementTypes); + addInheritedMembers(members, arrayType.properties); + setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexType, arrayType.numberIndexType); + } + function resolveAnonymousTypeMembers(type: ObjectType) { var symbol = type.symbol; if (symbol.flags & SymbolFlags.TypeLiteral) { @@ -1877,6 +1907,9 @@ module ts { else if (type.flags & TypeFlags.Anonymous) { resolveAnonymousTypeMembers(type); } + else if (type.flags & TypeFlags.Tuple) { + resolveTupleTypeMembers(type); + } else { resolveTypeReferenceMembers(type); } @@ -2241,7 +2274,7 @@ module ts { if (type.flags & (TypeFlags.Class | TypeFlags.Interface) && type.flags & TypeFlags.Reference) { var typeParameters = (type).typeParameters; if (node.typeArguments && node.typeArguments.length === typeParameters.length) { - type = createTypeReference(type, map(node.typeArguments, t => getTypeFromTypeNode(t))); + type = createTypeReference(type, map(node.typeArguments, getTypeFromTypeNode)); } else { error(node, Diagnostics.Generic_type_0_requires_1_type_argument_s, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType), typeParameters.length); @@ -2327,6 +2360,24 @@ module ts { return links.resolvedType; } + function createTupleType(elementTypes: Type[]) { + var id = getTypeListId(elementTypes); + var type = tupleTypes[id]; + if (!type) { + type = tupleTypes[id] = createObjectType(TypeFlags.Tuple); + type.elementTypes = elementTypes; + } + return type; + } + + function getTypeFromTupleTypeNode(node: TupleTypeNode): Type { + var links = getNodeLinks(node); + if (!links.resolvedType) { + links.resolvedType = createTupleType(map(node.elementTypes, getTypeFromTypeNode)); + } + return links.resolvedType; + } + function getTypeFromTypeLiteralNode(node: TypeLiteralNode): Type { var links = getNodeLinks(node); if (!links.resolvedType) { @@ -2371,6 +2422,8 @@ module ts { return getTypeFromTypeQueryNode(node); case SyntaxKind.ArrayType: return getTypeFromArrayTypeNode(node); + case SyntaxKind.TupleType: + return getTypeFromTupleTypeNode(node); case SyntaxKind.TypeLiteral: return getTypeFromTypeLiteralNode(node); // This function assumes that an identifier or qualified name is a type expression @@ -2532,6 +2585,9 @@ module ts { if (type.flags & TypeFlags.Reference) { return createTypeReference((type).target, instantiateList((type).typeArguments, mapper, instantiateType)); } + if (type.flags & TypeFlags.Tuple) { + return createTupleType(instantiateList((type).elementTypes, mapper, instantiateType)); + } } return type; } @@ -3182,7 +3238,6 @@ module ts { while (isArrayType(type)) { type = (type).typeArguments[0]; } - return type; } @@ -3331,9 +3386,9 @@ module ts { inferFromTypes(sourceTypes[i], targetTypes[i]); } } - else if (source.flags & TypeFlags.ObjectType && (target.flags & TypeFlags.Reference || (target.flags & TypeFlags.Anonymous) && - target.symbol && target.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral))) { - // If source is an object type, and target is a type reference, the type of a method, or a type literal, infer from members + else if (source.flags & TypeFlags.ObjectType && (target.flags & (TypeFlags.Reference | TypeFlags.Tuple) || + (target.flags & TypeFlags.Anonymous) && target.symbol && target.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral))) { + // If source is an object type, and target is a type reference, a tuple type, the type of a method, or a type literal, infer from members if (!isInProcess(source, target) && isWithinDepthLimit(source, sourceStack) && isWithinDepthLimit(target, targetStack)) { if (depth === 0) { sourceStack = []; @@ -3684,6 +3739,10 @@ module ts { return undefined; } + // In a variable, parameter or property declaration with a type annotation, the contextual type of an initializer + // expression is the type of the variable, parameter or property. In a parameter declaration of a contextually + // typed function expression, the contextual type of an initializer expression is the contextual type of the + // parameter. function getContextualTypeForInitializerExpression(node: Expression): Type { var declaration = node.parent; if (node === declaration.initializer) { @@ -3715,6 +3774,7 @@ module ts { return undefined; } + // In a typed function call, an argument expression is contextually typed by the type of the corresponding parameter. function getContextualTypeForArgument(node: Expression): Type { var callExpression = node.parent; var argIndex = indexOf(callExpression.arguments, node); @@ -3729,11 +3789,14 @@ module ts { var binaryExpression = node.parent; var operator = binaryExpression.operator; if (operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) { + // In an assignment expression, the right operand is contextually typed by the type of the left operand. if (node === binaryExpression.right) { return checkExpression(binaryExpression.left); } } else if (operator === SyntaxKind.BarBarToken) { + // When an || expression has a contextual type, the operands are contextually typed by that type. When an || + // expression has no contextual type, the right operand is contextually typed by the type of the left operand. var type = getContextualType(binaryExpression); if (!type && node === binaryExpression.right) { type = checkExpression(binaryExpression.left); @@ -3743,6 +3806,9 @@ module ts { return undefined; } + // In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of + // the matching property in T, if one exists. Otherwise, it is the type of the numeric index signature in T, if one + // exists. Otherwise, it is the type of the string index signature in T, if one exists. function getContextualTypeForPropertyExpression(node: Expression): Type { var declaration = node.parent; var objectLiteral = declaration.parent; @@ -3758,17 +3824,31 @@ module ts { return undefined; } + // In an array literal contextually typed by a type T, the contextual type of an element expression at index N is + // the type of the property with the numeric name N in T, if one exists. Otherwise, it is the type of the numeric + // index signature in T, if one exists. function getContextualTypeForElementExpression(node: Expression): Type { var arrayLiteral = node.parent; var type = getContextualType(arrayLiteral); - return type ? getIndexTypeOfType(type, IndexKind.Number) : undefined; + if (type) { + var index = indexOf(arrayLiteral.elements, node); + var prop = getPropertyOfType(type, "" + index); + if (prop) { + return getTypeOfSymbol(prop); + } + return getIndexTypeOfType(type, IndexKind.Number); + } + return undefined; } + // In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type. function getContextualTypeForConditionalOperand(node: Expression): Type { var conditional = node.parent; return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional) : undefined; } + // Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily + // be "pushed" onto a node using the contextualType property. function getContextualType(node: Expression): Type { if (node.contextualType) { return node.contextualType; @@ -3820,17 +3900,26 @@ module ts { } function checkArrayLiteral(node: ArrayLiteral, contextualMapper?: TypeMapper): Type { + var contextualType = getContextualType(node); + var elements = node.elements; var elementTypes: Type[] = []; - forEach(node.elements, element => { - if (element.kind !== SyntaxKind.OmittedExpression) { - var type = checkExpression(element, contextualMapper); - if (!contains(elementTypes, type)) elementTypes.push(type); + var isTupleLiteral: boolean = false; + for (var i = 0; i < elements.length; i++) { + if (contextualType && getPropertyOfType(contextualType, "" + i)) { + isTupleLiteral = true; } - }); - var contextualType = isInferentialContext(contextualMapper) ? undefined : getContextualType(node); - var contextualElementType = contextualType && getIndexTypeOfType(contextualType, IndexKind.Number); - var elementType = getBestCommonType(elementTypes, contextualElementType, true); - if (!elementType) elementType = elementTypes.length ? emptyObjectType : undefinedType; + var element = elements[i]; + var type = element.kind !== SyntaxKind.OmittedExpression ? checkExpression(element, contextualMapper) : undefinedType; + elementTypes.push(type); + } + if (isTupleLiteral) { + 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; + } return createArrayType(elementType); } @@ -3898,12 +3987,14 @@ module ts { } } + // If a symbol is a synthesized symbol with no value declaration, we assume it is a property. Example of this are the synthesized + // '.prototype' property as well as synthesized tuple index properties. function getDeclarationKindFromSymbol(s: Symbol) { - return s.flags & SymbolFlags.Prototype ? SyntaxKind.Property : s.valueDeclaration.kind; + return s.valueDeclaration ? s.valueDeclaration.kind : SyntaxKind.Property; } function getDeclarationFlagsFromSymbol(s: Symbol) { - return s.flags & SymbolFlags.Prototype ? NodeFlags.Public | NodeFlags.Static : s.valueDeclaration.flags; + return s.valueDeclaration ? s.valueDeclaration.flags : s.flags & SymbolFlags.Prototype ? NodeFlags.Public | NodeFlags.Static : 0; } function checkPropertyAccess(node: PropertyAccess) { @@ -5171,7 +5262,11 @@ module ts { } function checkArrayType(node: ArrayTypeNode) { - getTypeFromArrayTypeNode(node); + checkSourceElement(node.elementType); + } + + function checkTupleType(node: TupleTypeNode) { + forEach(node.elementTypes, checkSourceElement); } function isPrivateWithinAmbient(node: Node): boolean { @@ -6420,6 +6515,8 @@ module ts { return checkTypeLiteral(node); case SyntaxKind.ArrayType: return checkArrayType(node); + case SyntaxKind.TupleType: + return checkTupleType(node); case SyntaxKind.FunctionDeclaration: return checkFunctionDeclaration(node); case SyntaxKind.Block: diff --git a/src/compiler/core.ts b/src/compiler/core.ts index c549231eacc64..28a0a7bb6a315 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -19,8 +19,7 @@ module ts { export function contains(array: T[], value: T): boolean { if (array) { - var len = array.length; - for (var i = 0; i < len; i++) { + for (var i = 0, len = array.length; i < len; i++) { if (array[i] === value) { return true; } @@ -31,8 +30,7 @@ module ts { export function indexOf(array: T[], value: T): number { if (array) { - var len = array.length; - for (var i = 0; i < len; i++) { + for (var i = 0, len = array.length; i < len; i++) { if (array[i] === value) { return i; } @@ -42,9 +40,8 @@ module ts { } export function filter(array: T[], f: (x: T) => boolean): T[] { - var result: T[]; if (array) { - result = []; + var result: T[] = []; for (var i = 0, len = array.length; i < len; i++) { var item = array[i]; if (f(item)) { @@ -56,11 +53,9 @@ module ts { } export function map(array: T[], f: (x: T) => U): U[] { - var result: U[]; if (array) { - result = []; - var len = array.length; - for (var i = 0; i < len; i++) { + var result: U[] = []; + for (var i = 0, len = array.length; i < len; i++) { result.push(f(array[i])); } } @@ -73,6 +68,17 @@ module ts { return array1.concat(array2); } + export function uniqueElements(array: T[]): T[] { + if (array) { + var result: T[] = []; + for (var i = 0, len = array.length; i < len; i++) { + var item = array[i]; + if (!contains(result, item)) result.push(item); + } + } + return result; + } + export function sum(array: any[], prop: string): number { var result = 0; for (var i = 0; i < array.length; i++) { diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 40d27c93dd01e..04dbafc254e84 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -84,6 +84,7 @@ module ts { An_object_literal_cannot_have_property_and_accessor_with_the_same_name: { code: 1119, category: DiagnosticCategory.Error, key: "An object literal cannot have property and accessor with the same name." }, An_export_assignment_cannot_have_modifiers: { code: 1120, category: DiagnosticCategory.Error, key: "An export assignment cannot have modifiers." }, Octal_literals_are_not_allowed_in_strict_mode: { code: 1121, category: DiagnosticCategory.Error, key: "Octal literals are not allowed in strict mode." }, + A_tuple_type_element_list_cannot_be_empty: { code: 1122, category: DiagnosticCategory.Error, key: "A tuple type element list cannot be empty." }, Variable_declaration_list_cannot_be_empty: { code: 1123, category: DiagnosticCategory.Error, key: "Variable declaration list cannot be empty." }, Digit_expected: { code: 1124, category: DiagnosticCategory.Error, key: "Digit expected." }, Hexadecimal_digit_expected: { code: 1125, category: DiagnosticCategory.Error, key: "Hexadecimal digit expected." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 3c67e8de22430..6c78ec6bd5db9 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -327,6 +327,10 @@ "category": "Error", "code": 1121 }, + "A tuple type element list cannot be empty.": { + "category": "Error", + "code": 1122 + }, "Variable declaration list cannot be empty.": { "category": "Error", "code": 1123 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index be9047f3568c8..25ca24ae61b66 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -228,6 +228,8 @@ module ts { return children((node).members); case SyntaxKind.ArrayType: return child((node).elementType); + case SyntaxKind.TupleType: + return children((node).elementTypes); case SyntaxKind.ArrayLiteral: return children((node).elements); case SyntaxKind.ObjectLiteral: @@ -520,6 +522,7 @@ module ts { Parameters, // Parameters in parameter list TypeParameters, // Type parameters in type parameter list TypeArguments, // Type arguments in type argument list + TupleElementTypes, // Element types in tuple element type list Count // Number of parsing contexts } @@ -547,6 +550,7 @@ module ts { case ParsingContext.Parameters: return Diagnostics.Parameter_declaration_expected; case ParsingContext.TypeParameters: return Diagnostics.Type_parameter_declaration_expected; case ParsingContext.TypeArguments: return Diagnostics.Type_argument_expected; + case ParsingContext.TupleElementTypes: return Diagnostics.Type_expected; } }; @@ -1015,6 +1019,7 @@ module ts { case ParsingContext.Parameters: return isParameter(); case ParsingContext.TypeArguments: + case ParsingContext.TupleElementTypes: return isType(); } @@ -1050,6 +1055,7 @@ module ts { // Tokens other than ')' are here for better error recovery return token === SyntaxKind.CloseParenToken || token === SyntaxKind.SemicolonToken; case ParsingContext.ArrayLiteralMembers: + case ParsingContext.TupleElementTypes: return token === SyntaxKind.CloseBracketToken; case ParsingContext.Parameters: // Tokens other than ')' and ']' (the latter for index signatures) are here for better error recovery @@ -1570,6 +1576,17 @@ module ts { return finishNode(node); } + function parseTupleType(): TupleTypeNode { + var node = createNode(SyntaxKind.TupleType); + var startTokenPos = scanner.getTokenPos(); + var startErrorCount = file.syntacticErrors.length; + node.elementTypes = parseBracketedList(ParsingContext.TupleElementTypes, parseType, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken); + if (!node.elementTypes.length && file.syntacticErrors.length === startErrorCount) { + grammarErrorAtPos(startTokenPos, scanner.getStartPos() - startTokenPos, Diagnostics.A_tuple_type_element_list_cannot_be_empty); + } + return finishNode(node); + } + function parseFunctionType(signatureKind: SyntaxKind): TypeLiteralNode { var node = createNode(SyntaxKind.TypeLiteral); var member = createNode(signatureKind); @@ -1600,6 +1617,8 @@ module ts { return parseTypeQuery(); case SyntaxKind.OpenBraceToken: return parseTypeLiteral(); + case SyntaxKind.OpenBracketToken: + return parseTupleType(); case SyntaxKind.OpenParenToken: case SyntaxKind.LessThanToken: return parseFunctionType(SyntaxKind.CallSignature); @@ -1623,6 +1642,7 @@ module ts { case SyntaxKind.VoidKeyword: case SyntaxKind.TypeOfKeyword: case SyntaxKind.OpenBraceToken: + case SyntaxKind.OpenBracketToken: case SyntaxKind.LessThanToken: case SyntaxKind.NewKeyword: return true; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 715d01926f0aa..bb8a106a81321 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -149,6 +149,7 @@ module ts { TypeQuery, TypeLiteral, ArrayType, + TupleType, // Expression ArrayLiteral, ObjectLiteral, @@ -219,7 +220,7 @@ module ts { FirstFutureReservedWord = ImplementsKeyword, LastFutureReservedWord = YieldKeyword, FirstTypeNode = TypeReference, - LastTypeNode = ArrayType, + LastTypeNode = TupleType, FirstPunctuation = OpenBraceToken, LastPunctuation = CaretEqualsToken } @@ -320,6 +321,10 @@ module ts { elementType: TypeNode; } + export interface TupleTypeNode extends TypeNode { + elementTypes: NodeArray; + } + export interface StringLiteralTypeNode extends TypeNode { text: string; } @@ -803,13 +808,14 @@ module ts { Class = 0x00000400, // Class Interface = 0x00000800, // Interface Reference = 0x00001000, // Generic type reference - Anonymous = 0x00002000, // Anonymous - FromSignature = 0x00004000, // Created for signature assignment check + Tuple = 0x00002000, // Tuple + Anonymous = 0x00004000, // Anonymous + FromSignature = 0x00008000, // Created for signature assignment check Intrinsic = Any | String | Number | Boolean | Void | Undefined | Null, StringLike = String | StringLiteral, NumberLike = Number | Enum, - ObjectType = Class | Interface | Reference | Anonymous + ObjectType = Class | Interface | Reference | Tuple | Anonymous } // Properties common to all types @@ -862,6 +868,11 @@ module ts { openReferenceChecks: Map; // Open type reference check cache } + export interface TupleType extends ObjectType { + elementTypes: Type[]; // Element types + baseArrayType: TypeReference; // Array where T is best common type of element types + } + // Resolved object type export interface ResolvedObjectType extends ObjectType { members: SymbolTable; // Properties by name diff --git a/tests/baselines/reference/tupleTypes.errors.txt b/tests/baselines/reference/tupleTypes.errors.txt new file mode 100644 index 0000000000000..1e1ebd07292ae --- /dev/null +++ b/tests/baselines/reference/tupleTypes.errors.txt @@ -0,0 +1,87 @@ +==== tests/cases/compiler/tupleTypes.ts (9 errors) ==== + var v1: []; // Error + ~~ +!!! A tuple type element list cannot be empty. + var v2: [number]; + var v3: [number, string]; + var v4: [number, [string, string]]; + + var t: [number, string]; + var t0 = t[0]; // number + var t0: number; + var t1 = t[1]; // string + var t1: string; + var t2 = t[2]; // {} + var t2: {}; + + t = []; // Error + ~ +!!! Type '{}[]' is not assignable to type '[number, string]': +!!! Property '0' is missing in type '{}[]'. + t = [1]; // Error + ~ +!!! Type '[number]' is not assignable to type '[number, string]': +!!! Property '1' is missing in type '[number]'. + t = [1, "hello"]; // Ok + t = ["hello", 1]; // Error + ~ +!!! Type '[string, number]' is not assignable to type '[number, string]': +!!! Types of property '0' are incompatible: +!!! Type 'string' is not assignable to type 'number'. + t = [1, "hello", 2]; // Ok + + var tf: [string, (x: string) => number] = ["hello", x => x.length]; + + declare function ff(a: T, b: [T, (x: T) => U]): U; + var ff1 = ff("hello", ["foo", x => x.length]); + var ff1: number; + + function tuple2(item0: T0, item1: T1): [T0, T1]{ + return [item0, item1]; + } + + var tt = tuple2(1, "string"); + var tt0 = tt[0]; + var tt0: number; + var tt1 = tt[1]; + var tt1: string; + var tt2 = tt[2]; + var tt2: {}; + + tt = tuple2(1, undefined); + tt = [1, undefined]; + tt = [undefined, undefined]; + tt = []; // Error + ~~ +!!! Type '{}[]' is not assignable to type '[number, string]'. + + var a: number[]; + var a1: [number, string]; + var a2: [number, number]; + var a3: [number, {}]; + a = a1; // Error + ~ +!!! Type '[number, string]' is not assignable to type 'number[]': +!!! Types of property 'pop' are incompatible: +!!! Type '() => {}' is not assignable to type '() => number': +!!! Type '{}' is not assignable to type 'number'. + a = a2; + a = a3; // Error + ~ +!!! Type '[number, {}]' is not assignable to type 'number[]': +!!! Types of property 'pop' are incompatible: +!!! Type '() => {}' is not assignable to type '() => number': +!!! Type '{}' is not assignable to type 'number'. + a1 = a2; // Error + ~~ +!!! Type '[number, number]' is not assignable to type '[number, string]': +!!! Types of property '1' are incompatible: +!!! Type 'number' is not assignable to type 'string'. + a1 = a3; // Error + ~~ +!!! Type '[number, {}]' is not assignable to type '[number, string]': +!!! Types of property '1' are incompatible: +!!! Type '{}' is not assignable to type 'string'. + a3 = a1; + a3 = a2; + \ No newline at end of file diff --git a/tests/baselines/reference/typeName1.errors.txt b/tests/baselines/reference/typeName1.errors.txt index 92b3279ab812d..9c2b8c3797fea 100644 --- a/tests/baselines/reference/typeName1.errors.txt +++ b/tests/baselines/reference/typeName1.errors.txt @@ -1,4 +1,4 @@ -==== tests/cases/compiler/typeName1.ts (16 errors) ==== +==== tests/cases/compiler/typeName1.ts (17 errors) ==== interface I { k; } @@ -55,6 +55,8 @@ ~~~ !!! Type 'number' is not assignable to type '{ z: I; x: boolean; y: (s: string) => boolean; w: { (): boolean; [x: string]: { x: any; y: any; }; [x: number]: { x: any; y: any; }; z: I; }; }[][]': !!! Property 'length' is missing in type 'Number'. + ~~~~ +!!! Property 'z' of type 'I' is not assignable to string index type '{ x: any; y: any; }'. var x13:{ new(): number; new(n:number):number; x: string; w: {y: number;}; (): {}; } = 3; ~~~ !!! Type 'number' is not assignable to type '{ (): {}; new (): number; new (n: number): number; x: string; w: { y: number; }; }': diff --git a/tests/cases/compiler/tupleTypes.ts b/tests/cases/compiler/tupleTypes.ts new file mode 100644 index 0000000000000..3b22c284cb1c8 --- /dev/null +++ b/tests/cases/compiler/tupleTypes.ts @@ -0,0 +1,53 @@ +var v1: []; // Error +var v2: [number]; +var v3: [number, string]; +var v4: [number, [string, string]]; + +var t: [number, string]; +var t0 = t[0]; // number +var t0: number; +var t1 = t[1]; // string +var t1: string; +var t2 = t[2]; // {} +var t2: {}; + +t = []; // Error +t = [1]; // Error +t = [1, "hello"]; // Ok +t = ["hello", 1]; // Error +t = [1, "hello", 2]; // Ok + +var tf: [string, (x: string) => number] = ["hello", x => x.length]; + +declare function ff(a: T, b: [T, (x: T) => U]): U; +var ff1 = ff("hello", ["foo", x => x.length]); +var ff1: number; + +function tuple2(item0: T0, item1: T1): [T0, T1]{ + return [item0, item1]; +} + +var tt = tuple2(1, "string"); +var tt0 = tt[0]; +var tt0: number; +var tt1 = tt[1]; +var tt1: string; +var tt2 = tt[2]; +var tt2: {}; + +tt = tuple2(1, undefined); +tt = [1, undefined]; +tt = [undefined, undefined]; +tt = []; // Error + +var a: number[]; +var a1: [number, string]; +var a2: [number, number]; +var a3: [number, {}]; +a = a1; // Error +a = a2; +a = a3; // Error +a1 = a2; // Error +a1 = a3; // Error +a3 = a1; +a3 = a2;