Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for tuple types (e.g. [number, string]) #428

Merged
merged 10 commits into from
Sep 15, 2014
Merged

Conversation

ahejlsberg
Copy link
Member

This commit adds support for typle types. A tuple type is written as a comma separated sequence of types enclosed in square brackets:

[T0, T1, ... , Tn]

A tuple type corresponds to an object type that extends Array<T>, where T is the best common type of the tuple element types, with a sequence of numerically named members:

{
    0: T0;
    1: T1;
    ...
    n: Tn;
}

When an array literal is contextually typed by a tuple type, each of the array literal expressions are contextually typed by the corresponding tuple element type, and the result is a tuple type:

var t: [number, string] = [1, "hello"];
t = [];                 // Error
t = [1];                // Error
t = [2, "test"];        // Ok
t = ["test", 2];        // Error
t = [2, "test", true];  // Ok

When a value of a tuple type is indexed with a numeric constant, the resulting type is that of the corresponding type element. For example:

var x: [number, string] = [1, "hello"];
var x0 = x[0];  // Type number
var x1 = x[1];  // Type string
var x2 = x[2];  // Type {}

A tuple type is assignable to a compatible array type. For example:

var a1: number[];
var a2: {}[];
var t1: [number, string];
var t2: [number, number];
a1 = t1;  // Error
a1 = t2;  // Ok
a2 = t1;  // Ok
a2 = t2;  // Ok

Type inference works as expected for tuple types:

function tuple2<T0, T1>(item0: T0, item1: T1): [T0, T1] {
    return [item0, item1];
}
var t = tuple2("test", true);
var t0 = t[0];  // string
var t1 = t[1];  // boolean

Next step is to support EcmaScript 6 style destructuring of objects, arrays, and tuples.

// 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 &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add newline after the &&

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup.

@CyrusNajmabadi
Copy link
Contributor

This looks good. but i'm not sure now is the right time to be working on new features when we haven't even reached parity yet with the existing compiler. We need to get things like the TypeWriter and what not back online before we make changes that might introduce regressions that we don't have the infrastructure in place to catch.

a1 = a2; // Error
a1 = a3; // Error
a3 = a1;
a3 = a2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be worth breaking this up into a few different files by concept (ex tupleTypeInference, etc).

We also need a lot more coverage here. We should have tests for things like

var x = 0;
var y = 3;
var tt = tuple2(1, "a");
var r1 = tt[x];
var r2 = tt[y];

Tuples crossing module boundaries, usages in class hierarchies with array types, assignability with nested tuples and nested arrays, etc.

@basarat
Copy link
Contributor

basarat commented Aug 12, 2014

♨️ 😎

@JsonFreeman
Copy link
Contributor

Looks good, although please validate this with typeWriter (not online yet) before merging into master

@@ -2037,7 +2070,7 @@ module ts {
if (type.flags & (TypeFlags.Class | TypeFlags.Interface) && type.flags & TypeFlags.Reference) {
var typeParameters = (<InterfaceType>type).typeParameters;
if (node.typeArguments && node.typeArguments.length === typeParameters.length) {
type = createTypeReference(<GenericType>type, map(node.typeArguments, t => getTypeFromTypeNode(t)));
type = createTypeReference(<GenericType>type, map(node.typeArguments, getTypeFromTypeNode));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good ol' η-reduction.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

someone ate my tea

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Someone eta my tea

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Conflicts:
	tests/baselines/reference/typeName1.errors.txt
…bsence

of numerically named properties and doesn't directly test for tuple types.
@ahejlsberg
Copy link
Member Author

I have revised the contextual typing logic for array literals based on the discussions we had at the design meeting. Specifically:

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. When at least one element expression is contextually typed by a numerically named property, the resulting type of the array literal expression is a tuple type. Otherwise the resulting type is an array type.

With these rules there's nothing special about tuple types when it comes to contextual typing, and manually defined tuple-like type works just as well. For example:

interface Tuple2<T0, T1> {
    0: T0;
    1: T1;
}
var t: Tuple2<number, string> = [1, "hello"];
var x = t[0];  // number
var y = t[1];  // string

@@ -149,6 +149,7 @@ module ts {
TypeQuery,
TypeLiteral,
ArrayType,
TupleType,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After you merge the changes from typeWriter (now in master), be sure to modify SyntaxKind.LastTypeNode accordingly.

ahejlsberg added a commit that referenced this pull request Sep 15, 2014
Adding support for tuple types (e.g. [number, string])
@ahejlsberg ahejlsberg merged commit 11b9118 into master Sep 15, 2014
@ahejlsberg ahejlsberg deleted the tupleTypes branch September 15, 2014 19:58
@yuit yuit mentioned this pull request Oct 4, 2014
@SlurpTheo
Copy link

SlurpTheo commented Nov 30, 2016

Was reading and wondered why ~Array#shift() is available on a Tuple Type?

const a2: [number, string] = [1, "2"];
const a3: string | number = a2.shift();	// ["2"] left in array
const a4: string = a2[0];		// ERROR: Type 'number' is not assignable to type 'string'.

Just got this in the (2.0) playground.

@aluanhaddad
Copy link
Contributor

aluanhaddad commented Dec 1, 2016

@SlurpTheo because Tuple is a subtype of Array.
The error is because unshift doesn't retain the tuple type.
Technically it is because unshift is available to a value of the type [number, string] by way of
[number, string] being a subtype of (number | string)[].

@microsoft microsoft locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants