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:
Nathan Shively-Sanders 2019-08-26 10:42:17 -07:00 committed by GitHub
parent b4417da646
commit 21f192367a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 35 additions and 22 deletions

View file

@ -80,11 +80,11 @@ namespace FourSlash {
// 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
const enum MetadataOptionNames {
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
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
symlink = "Symlink",
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
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
symlink = "symlink",
}
// List of allowed metadata names
@ -3281,7 +3281,7 @@ ${code}
function parseTestData(basePath: string, contents: string, fileName: string): FourSlashData {
// 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
const files: FourSlashFile[] = [];
@ -3293,6 +3293,7 @@ ${code}
// 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
const lines = contents.split("\n");
let i = 0;
const markerPositions = ts.createMap<Marker>();
const markers: Marker[] = [];
@ -3321,6 +3322,7 @@ ${code}
}
for (let line of lines) {
i++;
if (line.length > 0 && line.charAt(line.length - 1) === "\r") {
line = line.substr(0, line.length - 1);
}
@ -3329,11 +3331,15 @@ ${code}
const text = line.substr(4);
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) === "//") {
// Comment line, check for global/file @options and record them
const match = optionRegex.exec(line.substr(2));
if (match) {
const [key, value] = match.slice(1);
const key = match[1].toLowerCase();
const value = match[2];
if (!ts.contains(fileMetadataNames, key)) {
// Check if the match is already existed in the global options
if (globalOptions[key] !== undefined) {

View file

@ -717,13 +717,9 @@ namespace ts.codefix {
}
function inferTypeFromPropertyAssignment(assignment: PropertyAssignment | ShorthandPropertyAssignment, checker: TypeChecker, usageContext: UsageContext) {
const objectLiteral = isShorthandPropertyAssignment(assignment) ?
assignment.parent :
assignment.parent.parent;
const nodeWithRealType = isVariableDeclaration(objectLiteral.parent) ?
objectLiteral.parent :
objectLiteral;
const nodeWithRealType = isVariableDeclaration(assignment.parent.parent) ?
assignment.parent.parent :
assignment.parent;
addCandidateThisType(usageContext, checker.getTypeAtLocation(nodeWithRealType));
}

View file

@ -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;
}`
});

View file

@ -5,9 +5,6 @@
// @module: es2015
// @moduleResolution: node
// @Filename: /node_modules/@types/react/index.d.ts
////export = React;
////export as namespace React;
////declare namespace React {
//// export class Component { render(): JSX.Element | null; }
////}
@ -16,10 +13,6 @@
//// interface Element {}
//// }
////}
// @filename: a.tsx
//// import React from 'react';
////
//// export default function Component([|props |]) {
//// if (props.isLoading) {
@ -37,4 +30,4 @@
//// }
verify.rangeAfterCodeFix("props: { isLoading: any; update: (arg0: any) => void; }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0);
verify.rangeAfterCodeFix("props: { isLoading: any; update: (arg0: any) => void; }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0);