mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 03:23:08 -06:00
Handles when property is renamed and is also part of destructuring assignment
Handles destructuring assignment part of #6312
This commit is contained in:
parent
6d43c02796
commit
ad916ab05d
@ -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)) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
20
tests/cases/fourslash/renameDestructuringAssignmentInFor.ts
Normal file
20
tests/cases/fourslash/renameDestructuringAssignmentInFor.ts
Normal 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);
|
||||
@ -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);
|
||||
@ -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);
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
@ -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);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user