Skip to content
Prev Previous commit
Next Next commit
Contextual typing of array literals is now based on the presence or a…
…bsence

of numerically named properties and doesn't directly test for tuple types.
  • Loading branch information
ahejlsberg committed Aug 16, 2014
commit 63b83e7c3fdd673ecdbfb6a4a533b0c963e55773
35 changes: 18 additions & 17 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3758,19 +3758,17 @@ module ts {
return undefined;
}

// In an array literal contextually typed by a type T, the contextual type of an element expression is the corresponding
// tuple element type in T, if one exists and T is a tuple type. Otherwise, it is the type of the numeric index signature
// in T, if one exists.
// 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 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

not sure what this is doing in the context of tuples. Can you clarify why this is necessary.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If the contextual type is a tuple type we want to return the tuple element type at the same index as the expression node.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry, I meant "clarify in the source code." These comments in the code review are not something people see when working with the code.

var arrayLiteral = <ArrayLiteral>node.parent;
var type = getContextualType(arrayLiteral);
if (type) {
if (type.flags & TypeFlags.Tuple) {
var index = indexOf(arrayLiteral.elements, node);
var prop = getPropertyOfType(type, "" + index);
if (prop) {
return getTypeOfSymbol(prop);
}
var index = indexOf(arrayLiteral.elements, node);
var prop = getPropertyOfType(type, "" + index);
if (prop) {
return getTypeOfSymbol(prop);
}
return getIndexTypeOfType(type, IndexKind.Number);
}
Expand Down Expand Up @@ -3837,21 +3835,24 @@ module ts {

function checkArrayLiteral(node: ArrayLiteral, contextualMapper?: TypeMapper): Type {
var contextualType = getContextualType(node);
var isTupleLiteral = contextualType && (contextualType.flags & TypeFlags.Tuple) !== 0;
var elements = node.elements;
var elementTypes: Type[] = [];
forEach(node.elements, element => {
var type = element.kind !== SyntaxKind.OmittedExpression ? checkExpression(element, contextualMapper) : undefinedType;
if (isTupleLiteral || !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 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(elementTypes, contextualElementType, true);
var elementType = getBestCommonType(uniqueElements(elementTypes), contextualElementType, true);
if (!elementType) {
elementType = elementTypes.length ? emptyObjectType : undefinedType;
elementType = elements.length ? emptyObjectType : undefinedType;
}
return createArrayType(elementType);
}
Expand Down
26 changes: 16 additions & 10 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ module ts {

export function contains<T>(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;
}
Expand All @@ -31,8 +30,7 @@ module ts {

export function indexOf<T>(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;
}
Expand All @@ -42,9 +40,8 @@ module ts {
}

export function filter<T>(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)) {
Expand All @@ -56,11 +53,9 @@ module ts {
}

export function map<T, U>(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]));
}
}
Expand All @@ -73,6 +68,17 @@ module ts {
return array1.concat(array2);
}

export function uniqueElements<T>(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++) {
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/tupleTypes.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

t = []; // Error
~
!!! Type '[]' is not assignable to type '[number, string]':
!!! Property '0' is missing in type '[]'.
!!! 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]':
Expand Down Expand Up @@ -53,7 +53,7 @@
tt = [undefined, undefined];
tt = []; // Error
~~
!!! Type '[]' is not assignable to type '[number, string]'.
!!! Type '{}[]' is not assignable to type '[number, string]'.

var a: number[];
var a1: [number, string];
Expand Down