mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 03:23:08 -06:00
Adding support for tuple types (e.g. [number, string])
This commit is contained in:
parent
c71e596ba8
commit
5b255243c9
@ -56,6 +56,7 @@ module ts {
|
||||
var globalBooleanType: ObjectType;
|
||||
var globalRegExpType: ObjectType;
|
||||
|
||||
var tupleTypes: Map<TupleType> = {};
|
||||
var stringLiteralTypes: Map<StringLiteralType> = {};
|
||||
|
||||
var fullTypeCheck = false;
|
||||
@ -619,15 +620,13 @@ 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<T>(enclosingDeclaration: Node, callback: (symbolTable: SymbolTable) => T): T {
|
||||
@ -843,6 +842,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(<TupleType>type);
|
||||
}
|
||||
else if (type.flags & TypeFlags.Anonymous) {
|
||||
writeAnonymousType(<ObjectType>type, allowFunctionOrConstructorTypeLiteral);
|
||||
}
|
||||
@ -855,6 +857,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
|
||||
@ -865,16 +876,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)) {
|
||||
@ -1649,6 +1661,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 = <TransientSymbol>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;
|
||||
var members = emptySymbols;
|
||||
@ -1682,6 +1711,9 @@ module ts {
|
||||
else if (type.flags & TypeFlags.Anonymous) {
|
||||
resolveAnonymousTypeMembers(<ObjectType>type);
|
||||
}
|
||||
else if (type.flags & TypeFlags.Tuple) {
|
||||
resolveTupleTypeMembers(<TupleType>type);
|
||||
}
|
||||
else {
|
||||
resolveTypeReferenceMembers(<TypeReference>type);
|
||||
}
|
||||
@ -2123,6 +2155,24 @@ module ts {
|
||||
return links.resolvedType;
|
||||
}
|
||||
|
||||
function createTupleType(elementTypes: Type[]) {
|
||||
var id = getTypeListId(elementTypes);
|
||||
var type = tupleTypes[id];
|
||||
if (!type) {
|
||||
type = tupleTypes[id] = <TupleType>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, t => getTypeFromTypeNode(t)));
|
||||
}
|
||||
return links.resolvedType;
|
||||
}
|
||||
|
||||
function getTypeFromTypeLiteralNode(node: TypeLiteralNode): Type {
|
||||
var links = getNodeLinks(node);
|
||||
if (!links.resolvedType) {
|
||||
@ -2172,6 +2222,8 @@ module ts {
|
||||
return getTypeFromTypeQueryNode(<TypeQueryNode>node);
|
||||
case SyntaxKind.ArrayType:
|
||||
return getTypeFromArrayTypeNode(<ArrayTypeNode>node);
|
||||
case SyntaxKind.TupleType:
|
||||
return getTypeFromTupleTypeNode(<TupleTypeNode>node);
|
||||
case SyntaxKind.TypeLiteral:
|
||||
return getTypeFromTypeLiteralNode(<TypeLiteralNode>node);
|
||||
default:
|
||||
@ -2327,6 +2379,9 @@ module ts {
|
||||
if (type.flags & TypeFlags.Reference) {
|
||||
return createTypeReference((<TypeReference>type).target, instantiateList((<TypeReference>type).typeArguments, mapper, instantiateType));
|
||||
}
|
||||
if (type.flags & TypeFlags.Tuple) {
|
||||
return createTupleType(instantiateList((<TupleType>type).elementTypes, mapper, instantiateType));
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
@ -3015,20 +3070,16 @@ module ts {
|
||||
while (isArrayType(type)) {
|
||||
type = (<GenericType>type).typeArguments[0];
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
function getWidenedTypeOfArrayLiteral(type: Type): Type {
|
||||
var elementType = (<TypeReference>type).typeArguments[0];
|
||||
var widenedType = getWidenedType(elementType);
|
||||
|
||||
type = elementType !== widenedType ? createArrayType(widenedType) : type;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/* If we are widening on a literal, then we may need to the 'node' parameter for reporting purposes */
|
||||
function getWidenedType(type: Type): Type {
|
||||
if (type.flags & (TypeFlags.Undefined | TypeFlags.Null)) {
|
||||
return anyType;
|
||||
@ -3125,9 +3176,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 = [];
|
||||
@ -3574,7 +3625,19 @@ module ts {
|
||||
function getContextualTypeForElementExpression(node: Expression): Type {
|
||||
var arrayLiteral = <ArrayLiteral>node.parent;
|
||||
var type = getContextualType(arrayLiteral);
|
||||
return type ? getIndexTypeOfType(type, IndexKind.Number) : undefined;
|
||||
if (type) {
|
||||
if (type.flags & TypeFlags.Tuple) {
|
||||
var index = indexOf(arrayLiteral.elements, node);
|
||||
if (index >= 0) {
|
||||
var prop = getPropertyOfType(type, "" + index);
|
||||
if (prop) {
|
||||
return getTypeOfSymbol(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
return getIndexTypeOfType(type, IndexKind.Number);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getContextualTypeForConditionalOperand(node: Expression): Type {
|
||||
@ -3633,17 +3696,23 @@ module ts {
|
||||
}
|
||||
|
||||
function checkArrayLiteral(node: ArrayLiteral, contextualMapper?: TypeMapper): Type {
|
||||
var contextualType = getContextualType(node);
|
||||
var isTupleLiteral = contextualType && (contextualType.flags & TypeFlags.Tuple) !== 0;
|
||||
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 type = element.kind !== SyntaxKind.OmittedExpression ? checkExpression(element, contextualMapper) : undefinedType;
|
||||
if (isTupleLiteral || !contains(elementTypes, type)) {
|
||||
elementTypes.push(type);
|
||||
}
|
||||
});
|
||||
var contextualType = isInferentialContext(contextualMapper) ? undefined : getContextualType(node);
|
||||
var contextualElementType = contextualType && getIndexTypeOfType(contextualType, IndexKind.Number);
|
||||
if (isTupleLiteral) {
|
||||
return createTupleType(elementTypes);
|
||||
}
|
||||
var contextualElementType = contextualType && !isInferentialContext(contextualMapper) ? getIndexTypeOfType(contextualType, IndexKind.Number) : undefined;
|
||||
var elementType = getBestCommonType(elementTypes, contextualElementType, true);
|
||||
if (!elementType) elementType = elementTypes.length ? emptyObjectType : undefinedType;
|
||||
if (!elementType) {
|
||||
elementType = elementTypes.length ? emptyObjectType : undefinedType;
|
||||
}
|
||||
return createArrayType(elementType);
|
||||
}
|
||||
|
||||
@ -3711,11 +3780,11 @@ module ts {
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -4991,7 +5060,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 {
|
||||
@ -6197,6 +6270,8 @@ module ts {
|
||||
return checkTypeLiteral(<TypeLiteralNode>node);
|
||||
case SyntaxKind.ArrayType:
|
||||
return checkArrayType(<ArrayTypeNode>node);
|
||||
case SyntaxKind.TupleType:
|
||||
return checkTupleType(<TupleTypeNode>node);
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
return checkFunctionDeclaration(<FunctionDeclaration>node);
|
||||
case SyntaxKind.Block:
|
||||
|
||||
@ -201,6 +201,8 @@ module ts {
|
||||
return children((<TypeLiteralNode>node).members);
|
||||
case SyntaxKind.ArrayType:
|
||||
return child((<ArrayTypeNode>node).elementType);
|
||||
case SyntaxKind.TupleType:
|
||||
return children((<TupleTypeNode>node).elementTypes);
|
||||
case SyntaxKind.ArrayLiteral:
|
||||
return children((<ArrayLiteral>node).elements);
|
||||
case SyntaxKind.ObjectLiteral:
|
||||
@ -352,6 +354,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
|
||||
}
|
||||
|
||||
@ -379,6 +382,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;
|
||||
}
|
||||
};
|
||||
|
||||
@ -837,6 +841,7 @@ module ts {
|
||||
case ParsingContext.Parameters:
|
||||
return isParameter();
|
||||
case ParsingContext.TypeArguments:
|
||||
case ParsingContext.TupleElementTypes:
|
||||
return isType();
|
||||
}
|
||||
|
||||
@ -872,6 +877,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
|
||||
@ -1390,6 +1396,17 @@ module ts {
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseTupleType(): TupleTypeNode {
|
||||
var node = <TupleTypeNode>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.Type_argument_list_cannot_be_empty);
|
||||
}
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseFunctionType(signatureKind: SyntaxKind): TypeLiteralNode {
|
||||
var node = <TypeLiteralNode>createNode(SyntaxKind.TypeLiteral);
|
||||
var member = <SignatureDeclaration>createNode(signatureKind);
|
||||
@ -1420,6 +1437,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);
|
||||
@ -1443,6 +1462,7 @@ module ts {
|
||||
case SyntaxKind.VoidKeyword:
|
||||
case SyntaxKind.TypeOfKeyword:
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
case SyntaxKind.LessThanToken:
|
||||
case SyntaxKind.NewKeyword:
|
||||
return true;
|
||||
|
||||
@ -149,6 +149,7 @@ module ts {
|
||||
TypeQuery,
|
||||
TypeLiteral,
|
||||
ArrayType,
|
||||
TupleType,
|
||||
// Expression
|
||||
ArrayLiteral,
|
||||
ObjectLiteral,
|
||||
@ -316,6 +317,10 @@ module ts {
|
||||
elementType: TypeNode;
|
||||
}
|
||||
|
||||
export interface TupleTypeNode extends TypeNode {
|
||||
elementTypes: NodeArray<TypeNode>;
|
||||
}
|
||||
|
||||
export interface StringLiteralTypeNode extends TypeNode {
|
||||
text: string;
|
||||
}
|
||||
@ -791,13 +796,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
|
||||
@ -850,6 +856,11 @@ module ts {
|
||||
openReferenceChecks: Map<boolean>; // Open type reference check cache
|
||||
}
|
||||
|
||||
export interface TupleType extends ObjectType {
|
||||
elementTypes: Type[]; // Element types
|
||||
baseArrayType: TypeReference; // Array<T> where T is best common type of element types
|
||||
}
|
||||
|
||||
// Resolved object type
|
||||
export interface ResolvedObjectType extends ObjectType {
|
||||
members: SymbolTable; // Properties by name
|
||||
|
||||
53
tests/cases/compiler/tupleTypes.ts
Normal file
53
tests/cases/compiler/tupleTypes.ts
Normal file
@ -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<T, U>(a: T, b: [T, (x: T) => U]): U;
|
||||
var ff1 = ff("hello", ["foo", x => x.length]);
|
||||
var ff1: number;
|
||||
|
||||
function tuple2<T0, T1>(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;
|
||||
Loading…
x
Reference in New Issue
Block a user