Fix infer from usage prop assignment (#33088)
* Add test case * Fix infer from usage property assignment Property assignment and shorthand property assignment were incorrectly treated differently; both have ObjectLiteralExpression as a parent, but the code previously assumed that property assignments had ObjectLiteralExpression as parent.parent. Also make fourslash directives case insensitive and less whitespace sensitive. * Add "incorrect 3-slash" error to fourslash parsing.
This commit is contained in:
parent
b4417da646
commit
21f192367a
|
@ -80,11 +80,11 @@ namespace FourSlash {
|
||||||
// To add additional option, add property into the testOptMetadataNames, refer the property in either globalMetadataNames or fileMetadataNames
|
// To add additional option, add property into the testOptMetadataNames, refer the property in either globalMetadataNames or fileMetadataNames
|
||||||
// Add cases into convertGlobalOptionsToCompilationsSettings function for the compiler to acknowledge such option from meta data
|
// Add cases into convertGlobalOptionsToCompilationsSettings function for the compiler to acknowledge such option from meta data
|
||||||
const enum MetadataOptionNames {
|
const enum MetadataOptionNames {
|
||||||
baselineFile = "BaselineFile",
|
baselineFile = "baselinefile",
|
||||||
emitThisFile = "emitThisFile", // This flag is used for testing getEmitOutput feature. It allows test-cases to indicate what file to be output in multiple files project
|
emitThisFile = "emitthisfile", // This flag is used for testing getEmitOutput feature. It allows test-cases to indicate what file to be output in multiple files project
|
||||||
fileName = "Filename",
|
fileName = "filename",
|
||||||
resolveReference = "ResolveReference", // This flag is used to specify entry file for resolve file references. The flag is only allow once per test file
|
resolveReference = "resolvereference", // This flag is used to specify entry file for resolve file references. The flag is only allow once per test file
|
||||||
symlink = "Symlink",
|
symlink = "symlink",
|
||||||
}
|
}
|
||||||
|
|
||||||
// List of allowed metadata names
|
// List of allowed metadata names
|
||||||
|
@ -3281,7 +3281,7 @@ ${code}
|
||||||
|
|
||||||
function parseTestData(basePath: string, contents: string, fileName: string): FourSlashData {
|
function parseTestData(basePath: string, contents: string, fileName: string): FourSlashData {
|
||||||
// Regex for parsing options in the format "@Alpha: Value of any sort"
|
// Regex for parsing options in the format "@Alpha: Value of any sort"
|
||||||
const optionRegex = /^\s*@(\w+): (.*)\s*/;
|
const optionRegex = /^\s*@(\w+):\s*(.*)\s*/;
|
||||||
|
|
||||||
// List of all the subfiles we've parsed out
|
// List of all the subfiles we've parsed out
|
||||||
const files: FourSlashFile[] = [];
|
const files: FourSlashFile[] = [];
|
||||||
|
@ -3293,6 +3293,7 @@ ${code}
|
||||||
// Note: IE JS engine incorrectly handles consecutive delimiters here when using RegExp split, so
|
// Note: IE JS engine incorrectly handles consecutive delimiters here when using RegExp split, so
|
||||||
// we have to string-based splitting instead and try to figure out the delimiting chars
|
// we have to string-based splitting instead and try to figure out the delimiting chars
|
||||||
const lines = contents.split("\n");
|
const lines = contents.split("\n");
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
const markerPositions = ts.createMap<Marker>();
|
const markerPositions = ts.createMap<Marker>();
|
||||||
const markers: Marker[] = [];
|
const markers: Marker[] = [];
|
||||||
|
@ -3321,6 +3322,7 @@ ${code}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let line of lines) {
|
for (let line of lines) {
|
||||||
|
i++;
|
||||||
if (line.length > 0 && line.charAt(line.length - 1) === "\r") {
|
if (line.length > 0 && line.charAt(line.length - 1) === "\r") {
|
||||||
line = line.substr(0, line.length - 1);
|
line = line.substr(0, line.length - 1);
|
||||||
}
|
}
|
||||||
|
@ -3329,11 +3331,15 @@ ${code}
|
||||||
const text = line.substr(4);
|
const text = line.substr(4);
|
||||||
currentFileContent = currentFileContent === undefined ? text : currentFileContent + "\n" + text;
|
currentFileContent = currentFileContent === undefined ? text : currentFileContent + "\n" + text;
|
||||||
}
|
}
|
||||||
|
else if (line.substr(0, 3) === "///" && currentFileContent !== undefined) {
|
||||||
|
throw new Error("Three-slash line in the middle of four-slash region at line " + i);
|
||||||
|
}
|
||||||
else if (line.substr(0, 2) === "//") {
|
else if (line.substr(0, 2) === "//") {
|
||||||
// Comment line, check for global/file @options and record them
|
// Comment line, check for global/file @options and record them
|
||||||
const match = optionRegex.exec(line.substr(2));
|
const match = optionRegex.exec(line.substr(2));
|
||||||
if (match) {
|
if (match) {
|
||||||
const [key, value] = match.slice(1);
|
const key = match[1].toLowerCase();
|
||||||
|
const value = match[2];
|
||||||
if (!ts.contains(fileMetadataNames, key)) {
|
if (!ts.contains(fileMetadataNames, key)) {
|
||||||
// Check if the match is already existed in the global options
|
// Check if the match is already existed in the global options
|
||||||
if (globalOptions[key] !== undefined) {
|
if (globalOptions[key] !== undefined) {
|
||||||
|
|
|
@ -717,13 +717,9 @@ namespace ts.codefix {
|
||||||
}
|
}
|
||||||
|
|
||||||
function inferTypeFromPropertyAssignment(assignment: PropertyAssignment | ShorthandPropertyAssignment, checker: TypeChecker, usageContext: UsageContext) {
|
function inferTypeFromPropertyAssignment(assignment: PropertyAssignment | ShorthandPropertyAssignment, checker: TypeChecker, usageContext: UsageContext) {
|
||||||
const objectLiteral = isShorthandPropertyAssignment(assignment) ?
|
const nodeWithRealType = isVariableDeclaration(assignment.parent.parent) ?
|
||||||
assignment.parent :
|
assignment.parent.parent :
|
||||||
assignment.parent.parent;
|
assignment.parent;
|
||||||
const nodeWithRealType = isVariableDeclaration(objectLiteral.parent) ?
|
|
||||||
objectLiteral.parent :
|
|
||||||
objectLiteral;
|
|
||||||
|
|
||||||
addCandidateThisType(usageContext, checker.getTypeAtLocation(nodeWithRealType));
|
addCandidateThisType(usageContext, checker.getTypeAtLocation(nodeWithRealType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
/// <reference path="fourslash.ts"/>
|
||||||
|
// @allowJs: true
|
||||||
|
// @checkJs: true
|
||||||
|
// @noEmit: true
|
||||||
|
// @filename:destruct.js
|
||||||
|
//// function [|formatter|](message) {
|
||||||
|
//// const { type } = false ? { type: message } : message;
|
||||||
|
//// }
|
||||||
|
verify.codeFix({
|
||||||
|
description: "Infer parameter types from usage",
|
||||||
|
index: 0,
|
||||||
|
newFileContent: `/**
|
||||||
|
* @param {{ type: any; }} message
|
||||||
|
*/
|
||||||
|
function formatter(message) {
|
||||||
|
const { type } = false ? { type: message } : message;
|
||||||
|
}`
|
||||||
|
});
|
|
@ -5,9 +5,6 @@
|
||||||
// @module: es2015
|
// @module: es2015
|
||||||
// @moduleResolution: node
|
// @moduleResolution: node
|
||||||
|
|
||||||
// @Filename: /node_modules/@types/react/index.d.ts
|
|
||||||
////export = React;
|
|
||||||
////export as namespace React;
|
|
||||||
////declare namespace React {
|
////declare namespace React {
|
||||||
//// export class Component { render(): JSX.Element | null; }
|
//// export class Component { render(): JSX.Element | null; }
|
||||||
////}
|
////}
|
||||||
|
@ -16,10 +13,6 @@
|
||||||
//// interface Element {}
|
//// interface Element {}
|
||||||
//// }
|
//// }
|
||||||
////}
|
////}
|
||||||
|
|
||||||
|
|
||||||
// @filename: a.tsx
|
|
||||||
//// import React from 'react';
|
|
||||||
////
|
////
|
||||||
//// export default function Component([|props |]) {
|
//// export default function Component([|props |]) {
|
||||||
//// if (props.isLoading) {
|
//// if (props.isLoading) {
|
||||||
|
|
Loading…
Reference in a new issue