Handles when property is renamed and is also part of destructuring assignment

Handles destructuring assignment part of #6312
This commit is contained in:
Sheetal Nandi 2016-04-07 15:41:42 -07:00
parent 6d43c02796
commit ad916ab05d
12 changed files with 288 additions and 57 deletions

View File

@ -83,6 +83,7 @@ namespace ts {
getShorthandAssignmentValueSymbol,
getExportSpecifierLocalTargetSymbol,
getTypeAtLocation: getTypeOfNode,
getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment,
typeToString,
getSymbolDisplayBuilder,
symbolToString,
@ -11690,39 +11691,43 @@ namespace ts {
function checkObjectLiteralAssignment(node: ObjectLiteralExpression, sourceType: Type, contextualMapper?: TypeMapper): Type {
const properties = node.properties;
for (const p of properties) {
if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) {
const name = <PropertyName>(<PropertyAssignment>p).name;
if (name.kind === SyntaxKind.ComputedPropertyName) {
checkComputedPropertyName(<ComputedPropertyName>name);
}
if (isComputedNonLiteralName(name)) {
continue;
}
checkObjectLiteralDestructuringPropertyAssignment(sourceType, p, contextualMapper);
}
return sourceType;
}
const text = getTextOfPropertyName(name);
const type = isTypeAny(sourceType)
? sourceType
: getTypeOfPropertyOfType(sourceType, text) ||
isNumericLiteralName(text) && getIndexTypeOfType(sourceType, IndexKind.Number) ||
getIndexTypeOfType(sourceType, IndexKind.String);
if (type) {
if (p.kind === SyntaxKind.ShorthandPropertyAssignment) {
checkDestructuringAssignment(<ShorthandPropertyAssignment>p, type);
}
else {
// non-shorthand property assignments should always have initializers
checkDestructuringAssignment((<PropertyAssignment>p).initializer, type);
}
function checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElement, contextualMapper?: TypeMapper) {
if (property.kind === SyntaxKind.PropertyAssignment || property.kind === SyntaxKind.ShorthandPropertyAssignment) {
const name = <PropertyName>(<PropertyAssignment>property).name;
if (name.kind === SyntaxKind.ComputedPropertyName) {
checkComputedPropertyName(<ComputedPropertyName>name);
}
if (isComputedNonLiteralName(name)) {
return undefined;
}
const text = getTextOfPropertyName(name);
const type = isTypeAny(objectLiteralType)
? objectLiteralType
: getTypeOfPropertyOfType(objectLiteralType, text) ||
isNumericLiteralName(text) && getIndexTypeOfType(objectLiteralType, IndexKind.Number) ||
getIndexTypeOfType(objectLiteralType, IndexKind.String);
if (type) {
if (property.kind === SyntaxKind.ShorthandPropertyAssignment) {
return checkDestructuringAssignment(<ShorthandPropertyAssignment>property, type);
}
else {
error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, typeToString(sourceType), declarationNameToString(name));
// non-shorthand property assignments should always have initializers
return checkDestructuringAssignment((<PropertyAssignment>property).initializer, type);
}
}
else {
error(p, Diagnostics.Property_assignment_expected);
error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, typeToString(objectLiteralType), declarationNameToString(name));
}
}
return sourceType;
else {
error(property, Diagnostics.Property_assignment_expected);
}
}
function checkArrayLiteralAssignment(node: ArrayLiteralExpression, sourceType: Type, contextualMapper?: TypeMapper): Type {
@ -11732,44 +11737,49 @@ namespace ts {
const elementType = checkIteratedTypeOrElementType(sourceType, node, /*allowStringInput*/ false) || unknownType;
const elements = node.elements;
for (let i = 0; i < elements.length; i++) {
const e = elements[i];
if (e.kind !== SyntaxKind.OmittedExpression) {
if (e.kind !== SyntaxKind.SpreadElementExpression) {
const propName = "" + i;
const type = isTypeAny(sourceType)
? sourceType
: isTupleLikeType(sourceType)
? getTypeOfPropertyOfType(sourceType, propName)
: elementType;
if (type) {
checkDestructuringAssignment(e, type, contextualMapper);
}
else {
if (isTupleType(sourceType)) {
error(e, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(sourceType), (<TupleType>sourceType).elementTypes.length, elements.length);
}
else {
error(e, Diagnostics.Type_0_has_no_property_1, typeToString(sourceType), propName);
}
}
checkArrayLiteralDestructuringElementAssignment(node, sourceType, elements[i], i, elementType, contextualMapper);
}
return sourceType;
}
function checkArrayLiteralDestructuringElementAssignment(node: ArrayLiteralExpression, sourceType: Type,
element: Expression, index: number, elementType: Type, contextualMapper?: TypeMapper) {
const elements = node.elements;
if (element.kind !== SyntaxKind.OmittedExpression) {
if (element.kind !== SyntaxKind.SpreadElementExpression) {
const propName = "" + index;
const type = isTypeAny(sourceType)
? sourceType
: isTupleLikeType(sourceType)
? getTypeOfPropertyOfType(sourceType, propName)
: elementType;
if (type) {
return checkDestructuringAssignment(element, type, contextualMapper);
}
else {
if (i < elements.length - 1) {
error(e, Diagnostics.A_rest_element_must_be_last_in_an_array_destructuring_pattern);
if (isTupleType(sourceType)) {
error(element, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(sourceType), (<TupleType>sourceType).elementTypes.length, elements.length);
}
else {
const restExpression = (<SpreadElementExpression>e).expression;
if (restExpression.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>restExpression).operatorToken.kind === SyntaxKind.EqualsToken) {
error((<BinaryExpression>restExpression).operatorToken, Diagnostics.A_rest_element_cannot_have_an_initializer);
}
else {
checkDestructuringAssignment(restExpression, createArrayType(elementType), contextualMapper);
}
error(element, Diagnostics.Type_0_has_no_property_1, typeToString(sourceType), propName);
}
}
}
else {
if (index < elements.length - 1) {
error(element, Diagnostics.A_rest_element_must_be_last_in_an_array_destructuring_pattern);
}
else {
const restExpression = (<SpreadElementExpression>element).expression;
if (restExpression.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>restExpression).operatorToken.kind === SyntaxKind.EqualsToken) {
error((<BinaryExpression>restExpression).operatorToken, Diagnostics.A_rest_element_cannot_have_an_initializer);
}
else {
return checkDestructuringAssignment(restExpression, createArrayType(elementType), contextualMapper);
}
}
}
}
return sourceType;
}
function checkDestructuringAssignment(exprOrAssignment: Expression | ShorthandPropertyAssignment, sourceType: Type, contextualMapper?: TypeMapper): Type {
@ -16431,6 +16441,34 @@ namespace ts {
return unknownType;
}
function getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(expr: Expression): Type {
// If this is from "for of"
// for ( { a } of elemns) {
// }
if (expr.parent.kind === SyntaxKind.ForOfStatement) {
const iteratedType = checkRightHandSideOfForOf((<ForOfStatement>expr.parent).expression);
return checkDestructuringAssignment(expr, iteratedType || unknownType);
}
// If this is from "for" initializer
// for ({a } = elems[0];.....) { }
if (expr.parent.kind === SyntaxKind.BinaryExpression) {
const iteratedType = checkExpression((<BinaryExpression>expr.parent).right);
return checkDestructuringAssignment(expr, iteratedType || unknownType);
}
// If this is from nested object binding pattern
// for ({ skills: { primary, secondary } } = multiRobot, i = 0; i < 1; i++) {
if (expr.parent.kind === SyntaxKind.PropertyAssignment) {
const typeOfParentObjectLiteral = getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(<Expression>expr.parent.parent);
return checkObjectLiteralDestructuringPropertyAssignment(typeOfParentObjectLiteral, <ObjectLiteralElement>expr.parent);
}
// Array literal assignment - array destructuring pattern
Debug.assert(expr.parent.kind === SyntaxKind.ArrayLiteralExpression);
// [{ property1: p1, property2 }] = elems;
const typeOfArrayLiteral = getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(<Expression>expr.parent);
const elementType = checkIteratedTypeOrElementType(typeOfArrayLiteral, expr.parent, /*allowStringInput*/ false) || unknownType;
return checkArrayLiteralDestructuringElementAssignment(<ArrayLiteralExpression>expr.parent, typeOfArrayLiteral,
expr, indexOf((<ArrayLiteralExpression>expr.parent).elements, expr), elementType);
}
function getTypeOfExpression(expr: Expression): Type {
if (isRightSideOfQualifiedNameOrPropertyAccess(expr)) {

View File

@ -1735,6 +1735,7 @@ namespace ts {
getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: string): Symbol[];
getShorthandAssignmentValueSymbol(location: Node): Symbol;
getExportSpecifierLocalTargetSymbol(location: ExportSpecifier): Symbol;
getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(expression: Expression): Type;
getTypeAtLocation(node: Node): Type;
typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string;
symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string;

View File

@ -6087,6 +6087,19 @@ namespace ts {
// The search set contains at least the current symbol
let result = [symbol];
// If the location is name of property symbol from object literal destructuring pattern
// Search the property symbol
// for ( { property: p2 } of elems) { }
if (isNameOfPropertyAssignment(location) &&
location.parent.kind !== SyntaxKind.ShorthandPropertyAssignment &&
isArrayLiteralOrObjectLiteralDestructuringPattern(location.parent.parent)) {
const typeOfObjectLiteral = typeChecker.getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(<Expression>location.parent.parent);
if (typeOfObjectLiteral) {
const propertySymbol = typeChecker.getPropertyOfType(typeOfObjectLiteral, (<Identifier>location).text);
result.push(propertySymbol);
}
}
// If the symbol is an alias, add what it aliases to the list
// import {a} from "mod";
// export {a}
@ -6234,13 +6247,32 @@ namespace ts {
return getRelatedSymbol(searchSymbols, aliasedSymbol, referenceLocation);
}
// If the reference location is in an object literal, try to get the contextual type for the
// object literal, lookup the property symbol in the contextual type, and use this symbol to
// compare to our searchSymbol
if (isNameOfPropertyAssignment(referenceLocation)) {
return forEach(getPropertySymbolsFromContextualType(referenceLocation), contextualSymbol => {
const contexualSymbol = forEach(getPropertySymbolsFromContextualType(referenceLocation), contextualSymbol => {
return forEach(typeChecker.getRootSymbols(contextualSymbol), s => searchSymbols.indexOf(s) >= 0 ? s : undefined);
});
if (contexualSymbol) {
return contexualSymbol;
}
// If the reference location is name of property symbol from object literal destructuring pattern
// Compare it to search symbol something similar to 'property' from
// for ( { property: p2 } of elems) { }
if (isArrayLiteralOrObjectLiteralDestructuringPattern(referenceLocation.parent.parent)) {
// Do work to determine if this is property symbol corresponding to the search symbol
const typeOfObjectLiteral = typeChecker.getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(<Expression>referenceLocation.parent.parent);
if (typeOfObjectLiteral) {
const propertySymbol = typeChecker.getPropertyOfType(typeOfObjectLiteral, (<Identifier>referenceLocation).text);
if (searchSymbols.indexOf(propertySymbol) >= 0) {
return propertySymbol;
}
}
}
}
// If the reference location is the binding element and doesn't have property name

View File

@ -13,7 +13,7 @@
////for (var { [|property1|]: p1 } of elems) {
////}
////var p2;
////for ({ /*This should be referenced too*/property1 : p2 } of elems) {
////for ({ [|property1|] : p2 } of elems) {
////}
// Note: if this test ever changes, consider updating

View File

@ -0,0 +1,20 @@
/// <reference path='fourslash.ts' />
////interface I {
//// /*1*/[|property1|]: number;
//// property2: string;
////}
////var elems: I[];
////
////var p2: number, property1: number;
////for ({ [|property1|] } = elems[0]; p2 < 100; p2++) {
//// p2 = property1++;
////}
////for ({ /*2*/[|property1|]: p2 } = elems[0]; p2 < 100; p2++) {
////}
goTo.marker("1");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
goTo.marker("2");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);

View File

@ -0,0 +1,20 @@
/// <reference path='fourslash.ts' />
////interface I {
//// /*1*/[|property1|]: number;
//// property2: string;
////}
////var elems: I[];
////
////var property1: number, p2: number;
////for ({ [|property1|] } of elems) {
//// property1++;
////}
////for ({ /*2*/[|property1|]: p2 } of elems) {
////}
goTo.marker("1");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
goTo.marker("2");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);

View File

@ -0,0 +1,15 @@
/// <reference path='fourslash.ts' />
////interface I {
//// /*1*/[|property1|]: number;
//// property2: string;
////}
////var elems: I[], p1: number, property1: number;
////[{ /*2*/[|property1|]: p1 }] = elems;
////[{ [|property1|] }] = elems;
goTo.marker("1");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
goTo.marker("2");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);

View File

@ -0,0 +1,15 @@
/// <reference path='fourslash.ts' />
////interface I {
//// property1: number;
//// property2: string;
////}
////var elems: I[], p1: number, [|property1|]: number;
////[{ property1: p1 }] = elems;
////[{ [|property1|] }] = elems;
let ranges = test.ranges()
for (let range of ranges) {
goTo.position(range.start);
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
}

View File

@ -0,0 +1,22 @@
/// <reference path='fourslash.ts' />
////interface MultiRobot {
//// name: string;
//// skills: {
//// /*1*/[|primary|]: string;
//// secondary: string;
//// };
////}
////let multiRobot: MultiRobot;
////for ({ skills: { /*2*/[|primary|]: primaryA, secondary: secondaryA } } = multiRobot, i = 0; i < 1; i++) {
//// console.log(primaryA);
////}
////for ({ skills: { [|primary|], secondary } } = multiRobot, i = 0; i < 1; i++) {
//// console.log(primary);
////}
goTo.marker("1");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
goTo.marker("2");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);

View File

@ -0,0 +1,23 @@
/// <reference path='fourslash.ts' />
////interface MultiRobot {
//// name: string;
//// skills: {
//// primary: string;
//// secondary: string;
//// };
////}
////let multiRobot: MultiRobot, [|primary|]: string;
////for ({ skills: { primary: primaryA, secondary: secondaryA } } = multiRobot, i = 0; i < 1; i++) {
//// console.log(primaryA);
////}
////for ({ skills: { [|primary|], secondary } } = multiRobot, i = 0; i < 1; i++) {
//// console.log([|primary|]);
////}
let ranges = test.ranges()
for (let range of ranges) {
goTo.position(range.start);
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
}

View File

@ -0,0 +1,22 @@
/// <reference path='fourslash.ts' />
////interface MultiRobot {
//// name: string;
//// skills: {
//// /*1*/[|primary|]: string;
//// secondary: string;
//// };
////}
////let multiRobots: MultiRobot[];
////for ({ skills: { /*2*/[|primary|]: primaryA, secondary: secondaryA } } of multiRobots) {
//// console.log(primaryA);
////}
////for ({ skills: { [|primary|], secondary } } of multiRobots) {
//// console.log(primary);
////}
goTo.marker("1");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
goTo.marker("2");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);

View File

@ -0,0 +1,23 @@
/// <reference path='fourslash.ts' />
////interface MultiRobot {
//// name: string;
//// skills: {
//// primary: string;
//// secondary: string;
//// };
////}
////let multiRobots: MultiRobot[], [|primary|]: string;
////for ({ skills: { primary: primaryA, secondary: secondaryA } } of multiRobots) {
//// console.log(primaryA);
////}
////for ({ skills: { [|primary|], secondary } } of multiRobots) {
//// console.log([|primary|]);
////}
let ranges = test.ranges()
for (let range of ranges) {
goTo.position(range.start);
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
}