Quick fix for no-implicit-any errors to add explicit type annotation (#14786)
* Infer from usage quick fix * Change full function singature * Add property/element access support * Fix a few issues * Some cleanup * Expose getArrayType and getPromiseType * Switch to collecting all usage before infering * Infer array and promise type arguments * Handel enums in binary operators * consolidate usage of addCandidateTypes * Handel rest paramters * Properly handel `+=` and `+` inference for numbers and strings * Add print quickfixes debug helper * Add rest param tests * Add optional paramter tests * Handel set accessors * Support getters * Support no implicit any error for variable at use site * Support properties * Only offer quick fix if an infered type other than any is available * Rename functions * Move to a separate namespace * Check cancellation token * Cleanup * Check for accesibile symbols where serializing types * Remove JS support * Reorganize functions * Mark APIs as internal * Fix lint errors * Removed conflict markers. * Update 'createSymbol' to use '__String'. * Fixed most problems relating to '__String' and 'includeJsDocComments' in the fix itself. * Addressed most API changes. * Make all helpers internal * Use a diffrent writer and not the built-in single line write * Infer types for all parameters in a parameter list instead of one at a time * Accept baselines * Code review commments * Respond to code review comments
This commit is contained in:
parent
b5e6b890f1
commit
4487917f89
26 changed files with 893 additions and 9 deletions
|
@ -226,6 +226,23 @@ namespace ts {
|
|||
return tryFindAmbientModule(moduleName, /*withAugmentations*/ false);
|
||||
},
|
||||
getApparentType,
|
||||
getUnionType,
|
||||
createAnonymousType,
|
||||
createSignature,
|
||||
createSymbol,
|
||||
createIndexInfo,
|
||||
getAnyType: () => anyType,
|
||||
getStringType: () => stringType,
|
||||
getNumberType: () => numberType,
|
||||
createPromiseType,
|
||||
createArrayType,
|
||||
getBooleanType: () => booleanType,
|
||||
getVoidType: () => voidType,
|
||||
getUndefinedType: () => undefinedType,
|
||||
getNullType: () => nullType,
|
||||
getESSymbolType: () => esSymbolType,
|
||||
getNeverType: () => neverType,
|
||||
isSymbolAccessible,
|
||||
isArrayLikeType,
|
||||
getAllPossiblePropertiesOfTypes,
|
||||
getSuggestionForNonexistentProperty: (node, type) => getSuggestionForNonexistentProperty(node, type),
|
||||
|
@ -3675,6 +3692,7 @@ namespace ts {
|
|||
|
||||
function buildParameterDisplay(p: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
|
||||
const parameterNode = <ParameterDeclaration>p.valueDeclaration;
|
||||
|
||||
if (parameterNode ? isRestParameter(parameterNode) : isTransientSymbol(p) && p.isRestParameter) {
|
||||
writePunctuation(writer, SyntaxKind.DotDotDotToken);
|
||||
}
|
||||
|
|
|
@ -213,11 +213,13 @@ namespace ts {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
export function zipWith<T, U>(arrayA: ReadonlyArray<T>, arrayB: ReadonlyArray<U>, callback: (a: T, b: U, index: number) => void): void {
|
||||
export function zipWith<T, U, V>(arrayA: ReadonlyArray<T>, arrayB: ReadonlyArray<U>, callback: (a: T, b: U, index: number) => V): V[] {
|
||||
const result: V[] = [];
|
||||
Debug.assert(arrayA.length === arrayB.length);
|
||||
for (let i = 0; i < arrayA.length; i++) {
|
||||
callback(arrayA[i], arrayB[i], i);
|
||||
result.push(callback(arrayA[i], arrayB[i], i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function zipToMap<T>(keys: ReadonlyArray<string>, values: ReadonlyArray<T>): Map<T> {
|
||||
|
|
|
@ -3681,6 +3681,7 @@
|
|||
"category": "Message",
|
||||
"code": 90017
|
||||
},
|
||||
|
||||
"Disable checking for this file.": {
|
||||
"category": "Message",
|
||||
"code": 90018
|
||||
|
@ -3725,7 +3726,6 @@
|
|||
"category": "Message",
|
||||
"code": 90028
|
||||
},
|
||||
|
||||
"Convert function to an ES2015 class": {
|
||||
"category": "Message",
|
||||
"code": 95001
|
||||
|
@ -3734,34 +3734,36 @@
|
|||
"category": "Message",
|
||||
"code": 95002
|
||||
},
|
||||
|
||||
"Extract symbol": {
|
||||
"category": "Message",
|
||||
"code": 95003
|
||||
},
|
||||
|
||||
"Extract to {0} in {1}": {
|
||||
"category": "Message",
|
||||
"code": 95004
|
||||
},
|
||||
|
||||
"Extract function": {
|
||||
"category": "Message",
|
||||
"code": 95005
|
||||
},
|
||||
|
||||
"Extract constant": {
|
||||
"category": "Message",
|
||||
"code": 95006
|
||||
},
|
||||
|
||||
"Extract to {0} in enclosing scope": {
|
||||
"category": "Message",
|
||||
"code": 95007
|
||||
},
|
||||
|
||||
"Extract to {0} in {1} scope": {
|
||||
"category": "Message",
|
||||
"code": 95008
|
||||
},
|
||||
"Infer type of '{0}' from usage.": {
|
||||
"category": "Message",
|
||||
"code": 95009
|
||||
},
|
||||
"Infer parameter types from usage.": {
|
||||
"category": "Message",
|
||||
"code": 95010
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2695,6 +2695,24 @@ namespace ts {
|
|||
getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
|
||||
/* @internal */ getBaseConstraintOfType(type: Type): Type | undefined;
|
||||
|
||||
/* @internal */ getAnyType(): Type;
|
||||
/* @internal */ getStringType(): Type;
|
||||
/* @internal */ getNumberType(): Type;
|
||||
/* @internal */ getBooleanType(): Type;
|
||||
/* @internal */ getVoidType(): Type;
|
||||
/* @internal */ getUndefinedType(): Type;
|
||||
/* @internal */ getNullType(): Type;
|
||||
/* @internal */ getESSymbolType(): Type;
|
||||
/* @internal */ getNeverType(): Type;
|
||||
/* @internal */ getUnionType(types: Type[], subtypeReduction?: boolean): Type;
|
||||
/* @internal */ createArrayType(elementType: Type): Type;
|
||||
/* @internal */ createPromiseType(type: Type): Type;
|
||||
|
||||
/* @internal */ createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo, numberIndexInfo: IndexInfo): Type;
|
||||
/* @internal */ createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, parameters: Symbol[], resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasLiteralTypes: boolean): Signature;
|
||||
/* @internal */ createSymbol(flags: SymbolFlags, name: __String): TransientSymbol;
|
||||
/* @internal */ createIndexInfo(type: Type, isReadonly: boolean, declaration?: SignatureDeclaration): IndexInfo;
|
||||
/* @internal */ isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, shouldComputeAliasToMarkVisible: boolean): SymbolAccessibilityResult;
|
||||
/* @internal */ tryFindAmbientModuleWithoutAugmentations(moduleName: string): Symbol | undefined;
|
||||
|
||||
/* @internal */ getSymbolWalker(accept?: (symbol: Symbol) => boolean): SymbolWalker;
|
||||
|
|
|
@ -5649,6 +5649,14 @@ namespace ts {
|
|||
return node.kind >= SyntaxKind.FirstJSDocTagNode && node.kind <= SyntaxKind.LastJSDocTagNode;
|
||||
}
|
||||
|
||||
export function isSetAccessor(node: Node): node is SetAccessorDeclaration {
|
||||
return node.kind === SyntaxKind.SetAccessor;
|
||||
}
|
||||
|
||||
export function isGetAccessor(node: Node): node is GetAccessorDeclaration {
|
||||
return node.kind === SyntaxKind.GetAccessor;
|
||||
}
|
||||
|
||||
/** True if has jsdoc nodes attached to it. */
|
||||
/* @internal */
|
||||
export function hasJSDocNodes(node: Node): node is HasJSDoc {
|
||||
|
|
|
@ -13,3 +13,4 @@
|
|||
/// <reference path='importFixes.ts' />
|
||||
/// <reference path='disableJsDiagnostics.ts' />
|
||||
/// <reference path='helpers.ts' />
|
||||
/// <reference path='inferFromUsage.ts' />
|
||||
|
|
653
src/services/codefixes/inferFromUsage.ts
Normal file
653
src/services/codefixes/inferFromUsage.ts
Normal file
|
@ -0,0 +1,653 @@
|
|||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [
|
||||
// Variable declarations
|
||||
Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined.code,
|
||||
|
||||
// Variable uses
|
||||
Diagnostics.Variable_0_implicitly_has_an_1_type.code,
|
||||
|
||||
// Parameter declarations
|
||||
Diagnostics.Parameter_0_implicitly_has_an_1_type.code,
|
||||
Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code,
|
||||
|
||||
// Get Accessor declarations
|
||||
Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation.code,
|
||||
Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type.code,
|
||||
|
||||
// Set Accessor declarations
|
||||
Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation.code,
|
||||
|
||||
// Property declarations
|
||||
Diagnostics.Member_0_implicitly_has_an_1_type.code,
|
||||
],
|
||||
getCodeActions: getActionsForAddExplicitTypeAnnotation
|
||||
});
|
||||
|
||||
function getActionsForAddExplicitTypeAnnotation({ sourceFile, program, span: { start }, errorCode, cancellationToken }: CodeFixContext): CodeAction[] | undefined {
|
||||
const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false);
|
||||
let writer: StringSymbolWriter;
|
||||
|
||||
if (isInJavaScriptFile(token)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
switch (token.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
case SyntaxKind.DotDotDotToken:
|
||||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
// Allowed
|
||||
break;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const containingFunction = getContainingFunction(token);
|
||||
const checker = program.getTypeChecker();
|
||||
|
||||
switch (errorCode) {
|
||||
// Variable and Property declarations
|
||||
case Diagnostics.Member_0_implicitly_has_an_1_type.code:
|
||||
case Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined.code:
|
||||
return getCodeActionForVariableDeclaration(<PropertyDeclaration | PropertySignature | VariableDeclaration>token.parent);
|
||||
case Diagnostics.Variable_0_implicitly_has_an_1_type.code:
|
||||
return getCodeActionForVariableUsage(<Identifier>token);
|
||||
|
||||
// Parameter declarations
|
||||
case Diagnostics.Parameter_0_implicitly_has_an_1_type.code:
|
||||
if (isSetAccessor(containingFunction)) {
|
||||
return getCodeActionForSetAccessor(containingFunction);
|
||||
}
|
||||
// falls through
|
||||
case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code:
|
||||
return getCodeActionForParameters(<ParameterDeclaration>token.parent);
|
||||
|
||||
// Get Accessor declarations
|
||||
case Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation.code:
|
||||
case Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type.code:
|
||||
return isGetAccessor(containingFunction) ? getCodeActionForGetAccessor(containingFunction) : undefined;
|
||||
|
||||
// Set Accessor declarations
|
||||
case Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation.code:
|
||||
return isSetAccessor(containingFunction) ? getCodeActionForSetAccessor(containingFunction) : undefined;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
function getCodeActionForVariableDeclaration(declaration: VariableDeclaration | PropertyDeclaration | PropertySignature) {
|
||||
if (!isIdentifier(declaration.name)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const type = inferTypeForVariableFromUsage(declaration.name);
|
||||
const typeString = type && typeToString(type, declaration);
|
||||
|
||||
if (!typeString) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return createCodeActions(declaration.name.getText(), declaration.name.getEnd(), `: ${typeString}`);
|
||||
}
|
||||
|
||||
function getCodeActionForVariableUsage(token: Identifier) {
|
||||
const symbol = checker.getSymbolAtLocation(token);
|
||||
return symbol && symbol.valueDeclaration && getCodeActionForVariableDeclaration(<VariableDeclaration>symbol.valueDeclaration);
|
||||
}
|
||||
|
||||
function isApplicableFunctionForInference(declaration: FunctionLike): declaration is MethodDeclaration | FunctionDeclaration | ConstructorDeclaration {
|
||||
switch (declaration.kind) {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.Constructor:
|
||||
return true;
|
||||
case SyntaxKind.FunctionExpression:
|
||||
return !!(declaration as FunctionExpression).name;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getCodeActionForParameters(parameterDeclaration: ParameterDeclaration): CodeAction[] {
|
||||
if (!isIdentifier(parameterDeclaration.name) || !isApplicableFunctionForInference(containingFunction)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const types = inferTypeForParametersFromUsage(containingFunction) ||
|
||||
map(containingFunction.parameters, p => isIdentifier(p.name) && inferTypeForVariableFromUsage(p.name));
|
||||
|
||||
if (!types) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const textChanges: TextChange[] = zipWith(containingFunction.parameters, types, (parameter, type) => {
|
||||
if (type && !parameter.type && !parameter.initializer) {
|
||||
const typeString = typeToString(type, containingFunction);
|
||||
return typeString ? {
|
||||
span: { start: parameter.end, length: 0 },
|
||||
newText: `: ${typeString}`
|
||||
} : undefined;
|
||||
}
|
||||
}).filter(c => !!c);
|
||||
|
||||
return textChanges.length ? [{
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Infer_parameter_types_from_usage), [parameterDeclaration.name.getText()]),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges
|
||||
}]
|
||||
}] : undefined;
|
||||
}
|
||||
|
||||
function getCodeActionForSetAccessor(setAccessorDeclaration: SetAccessorDeclaration) {
|
||||
const setAccessorParameter = setAccessorDeclaration.parameters[0];
|
||||
if (!setAccessorParameter || !isIdentifier(setAccessorDeclaration.name) || !isIdentifier(setAccessorParameter.name)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const type = inferTypeForVariableFromUsage(setAccessorDeclaration.name) ||
|
||||
inferTypeForVariableFromUsage(setAccessorParameter.name);
|
||||
const typeString = type && typeToString(type, containingFunction);
|
||||
if (!typeString) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return createCodeActions(setAccessorDeclaration.name.getText(), setAccessorParameter.name.getEnd(), `: ${typeString}`);
|
||||
}
|
||||
|
||||
function getCodeActionForGetAccessor(getAccessorDeclaration: GetAccessorDeclaration) {
|
||||
if (!isIdentifier(getAccessorDeclaration.name)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const type = inferTypeForVariableFromUsage(getAccessorDeclaration.name);
|
||||
const typeString = type && typeToString(type, containingFunction);
|
||||
if (!typeString) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const closeParenToken = getFirstChildOfKind(getAccessorDeclaration, sourceFile, SyntaxKind.CloseParenToken);
|
||||
return createCodeActions(getAccessorDeclaration.name.getText(), closeParenToken.getEnd(), `: ${typeString}`);
|
||||
}
|
||||
|
||||
function createCodeActions(name: string, start: number, typeString: string) {
|
||||
return [{
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Infer_type_of_0_from_usage), [name]),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{
|
||||
span: { start, length: 0 },
|
||||
newText: typeString
|
||||
}]
|
||||
}]
|
||||
}];
|
||||
}
|
||||
|
||||
function getReferences(token: PropertyName | Token<SyntaxKind.ConstructorKeyword>) {
|
||||
const references = FindAllReferences.findReferencedSymbols(
|
||||
program,
|
||||
cancellationToken,
|
||||
program.getSourceFiles(),
|
||||
token.getSourceFile(),
|
||||
token.getStart());
|
||||
|
||||
Debug.assert(!!references, "Found no references!");
|
||||
Debug.assert(references.length === 1, "Found more references than expected");
|
||||
|
||||
return map(references[0].references, r => <Identifier>getTokenAtPosition(program.getSourceFile(r.fileName), r.textSpan.start, /*includeJsDocComment*/ false));
|
||||
}
|
||||
|
||||
function inferTypeForVariableFromUsage(token: Identifier) {
|
||||
return InferFromReference.inferTypeFromReferences(getReferences(token), checker, cancellationToken);
|
||||
}
|
||||
|
||||
function inferTypeForParametersFromUsage(containingFunction: FunctionLikeDeclaration) {
|
||||
switch (containingFunction.kind) {
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
const isConstructor = containingFunction.kind === SyntaxKind.Constructor;
|
||||
const searchToken = isConstructor ?
|
||||
<Token<SyntaxKind.ConstructorKeyword>>getFirstChildOfKind(containingFunction, sourceFile, SyntaxKind.ConstructorKeyword) :
|
||||
containingFunction.name;
|
||||
if (searchToken) {
|
||||
return InferFromReference.inferTypeForParametersFromReferences(getReferences(searchToken), containingFunction, checker, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeAccessiblityWriter() {
|
||||
if (!writer) {
|
||||
let str = "";
|
||||
let typeIsAccessible = true;
|
||||
|
||||
const writeText: (text: string) => void = text => str += text;
|
||||
writer = {
|
||||
string: () => typeIsAccessible ? str : undefined,
|
||||
writeKeyword: writeText,
|
||||
writeOperator: writeText,
|
||||
writePunctuation: writeText,
|
||||
writeSpace: writeText,
|
||||
writeStringLiteral: writeText,
|
||||
writeParameter: writeText,
|
||||
writeProperty: writeText,
|
||||
writeSymbol: writeText,
|
||||
writeLine: () => str += " ",
|
||||
increaseIndent: noop,
|
||||
decreaseIndent: noop,
|
||||
clear: () => { str = ""; typeIsAccessible = true; },
|
||||
trackSymbol: (symbol, declaration, meaning) => {
|
||||
if (checker.isSymbolAccessible(symbol, declaration, meaning, /*shouldComputeAliasToMarkVisible*/ false).accessibility !== SymbolAccessibility.Accessible) {
|
||||
typeIsAccessible = false;
|
||||
}
|
||||
},
|
||||
reportInaccessibleThisError: () => { typeIsAccessible = false; },
|
||||
reportPrivateInBaseOfClassExpression: () => { typeIsAccessible = false; },
|
||||
};
|
||||
}
|
||||
writer.clear();
|
||||
return writer;
|
||||
}
|
||||
|
||||
function typeToString(type: Type, enclosingDeclaration: Declaration) {
|
||||
const writer = getTypeAccessiblityWriter();
|
||||
checker.getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration);
|
||||
return writer.string();
|
||||
}
|
||||
|
||||
function getFirstChildOfKind(node: Node, sourcefile: SourceFile, kind: SyntaxKind) {
|
||||
for (const child of node.getChildren(sourcefile)) {
|
||||
if (child.kind === kind) return child;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
namespace InferFromReference {
|
||||
interface CallContext {
|
||||
argumentTypes: Type[];
|
||||
returnType: UsageContext;
|
||||
}
|
||||
|
||||
interface UsageContext {
|
||||
isNumber?: boolean;
|
||||
isString?: boolean;
|
||||
isNumberOrString?: boolean;
|
||||
candidateTypes?: Type[];
|
||||
properties?: UnderscoreEscapedMap<UsageContext>;
|
||||
callContexts?: CallContext[];
|
||||
constructContexts?: CallContext[];
|
||||
numberIndexContext?: UsageContext;
|
||||
stringIndexContext?: UsageContext;
|
||||
}
|
||||
|
||||
export function inferTypeFromReferences(references: Identifier[], checker: TypeChecker, cancellationToken: CancellationToken): Type | undefined {
|
||||
const usageContext: UsageContext = {};
|
||||
for (const reference of references) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
inferTypeFromContext(reference, checker, usageContext);
|
||||
}
|
||||
return getTypeFromUsageContext(usageContext, checker);
|
||||
}
|
||||
|
||||
export function inferTypeForParametersFromReferences(references: Identifier[], declaration: FunctionLikeDeclaration, checker: TypeChecker, cancellationToken: CancellationToken): (Type | undefined)[] | undefined {
|
||||
if (declaration.parameters) {
|
||||
const usageContext: UsageContext = {};
|
||||
for (const reference of references) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
inferTypeFromContext(reference, checker, usageContext);
|
||||
}
|
||||
const isConstructor = declaration.kind === SyntaxKind.Constructor;
|
||||
const callContexts = isConstructor ? usageContext.constructContexts : usageContext.callContexts;
|
||||
if (callContexts) {
|
||||
const paramTypes: Type[] = [];
|
||||
for (let parameterIndex = 0; parameterIndex < declaration.parameters.length; parameterIndex++) {
|
||||
let types: Type[] = [];
|
||||
const isRestParameter = ts.isRestParameter(declaration.parameters[parameterIndex]);
|
||||
for (const callContext of callContexts) {
|
||||
if (callContext.argumentTypes.length > parameterIndex) {
|
||||
if (isRestParameter) {
|
||||
types = concatenate(types, map(callContext.argumentTypes.slice(parameterIndex), a => checker.getBaseTypeOfLiteralType(a)));
|
||||
}
|
||||
else {
|
||||
types.push(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[parameterIndex]));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (types.length) {
|
||||
const type = checker.getWidenedType(checker.getUnionType(types, /*subtypeReduction*/ true));
|
||||
paramTypes[parameterIndex] = isRestParameter ? checker.createArrayType(type) : type;
|
||||
}
|
||||
}
|
||||
return paramTypes;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function inferTypeFromContext(node: Expression, checker: TypeChecker, usageContext: UsageContext): void {
|
||||
while (isRightSideOfQualifiedNameOrPropertyAccess(node)) {
|
||||
node = <Expression>node.parent;
|
||||
}
|
||||
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
usageContext.isNumber = true;
|
||||
break;
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
inferTypeFromPrefixUnaryExpressionContext(<PrefixUnaryExpression>node.parent, usageContext);
|
||||
break;
|
||||
case SyntaxKind.BinaryExpression:
|
||||
inferTypeFromBinaryExpressionContext(node, <BinaryExpression>node.parent, checker, usageContext);
|
||||
break;
|
||||
case SyntaxKind.CaseClause:
|
||||
case SyntaxKind.DefaultClause:
|
||||
inferTypeFromSwitchStatementLabelContext(<CaseOrDefaultClause>node.parent, checker, usageContext);
|
||||
break;
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.NewExpression:
|
||||
if ((<CallExpression | NewExpression>node.parent).expression === node) {
|
||||
inferTypeFromCallExpressionContext(<CallExpression | NewExpression>node.parent, checker, usageContext);
|
||||
}
|
||||
else {
|
||||
inferTypeFromContextualType(node, checker, usageContext);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
inferTypeFromPropertyAccessExpressionContext(<PropertyAccessExpression>node.parent, checker, usageContext);
|
||||
break;
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
inferTypeFromPropertyElementExpressionContext(<ElementAccessExpression>node.parent, node, checker, usageContext);
|
||||
break;
|
||||
default:
|
||||
return inferTypeFromContextualType(node, checker, usageContext);
|
||||
}
|
||||
}
|
||||
|
||||
function inferTypeFromContextualType(node: Expression, checker: TypeChecker, usageContext: UsageContext): void {
|
||||
if (isPartOfExpression(node)) {
|
||||
addCandidateType(usageContext, checker.getContextualType(node));
|
||||
}
|
||||
}
|
||||
|
||||
function inferTypeFromPrefixUnaryExpressionContext(node: PrefixUnaryExpression, usageContext: UsageContext): void {
|
||||
switch (node.operator) {
|
||||
case SyntaxKind.PlusPlusToken:
|
||||
case SyntaxKind.MinusMinusToken:
|
||||
case SyntaxKind.MinusToken:
|
||||
case SyntaxKind.TildeToken:
|
||||
usageContext.isNumber = true;
|
||||
break;
|
||||
|
||||
case SyntaxKind.PlusToken:
|
||||
usageContext.isNumberOrString = true;
|
||||
break;
|
||||
|
||||
// case SyntaxKind.ExclamationToken:
|
||||
// no inferences here;
|
||||
}
|
||||
}
|
||||
|
||||
function inferTypeFromBinaryExpressionContext(node: Expression, parent: BinaryExpression, checker: TypeChecker, usageContext: UsageContext): void {
|
||||
switch (parent.operatorToken.kind) {
|
||||
// ExponentiationOperator
|
||||
case SyntaxKind.AsteriskAsteriskToken:
|
||||
|
||||
// MultiplicativeOperator
|
||||
case SyntaxKind.AsteriskToken:
|
||||
case SyntaxKind.SlashToken:
|
||||
case SyntaxKind.PercentToken:
|
||||
|
||||
// ShiftOperator
|
||||
case SyntaxKind.LessThanLessThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
||||
|
||||
// BitwiseOperator
|
||||
case SyntaxKind.AmpersandToken:
|
||||
case SyntaxKind.BarToken:
|
||||
case SyntaxKind.CaretToken:
|
||||
|
||||
// CompoundAssignmentOperator
|
||||
case SyntaxKind.MinusEqualsToken:
|
||||
case SyntaxKind.AsteriskAsteriskEqualsToken:
|
||||
case SyntaxKind.AsteriskEqualsToken:
|
||||
case SyntaxKind.SlashEqualsToken:
|
||||
case SyntaxKind.PercentEqualsToken:
|
||||
case SyntaxKind.AmpersandEqualsToken:
|
||||
case SyntaxKind.BarEqualsToken:
|
||||
case SyntaxKind.CaretEqualsToken:
|
||||
case SyntaxKind.LessThanLessThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
|
||||
|
||||
// AdditiveOperator
|
||||
case SyntaxKind.MinusToken:
|
||||
|
||||
// RelationalOperator
|
||||
case SyntaxKind.LessThanToken:
|
||||
case SyntaxKind.LessThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanToken:
|
||||
case SyntaxKind.GreaterThanEqualsToken:
|
||||
const operandType = checker.getTypeAtLocation(parent.left === node ? parent.right : parent.left);
|
||||
if (operandType.flags & TypeFlags.EnumLike) {
|
||||
addCandidateType(usageContext, operandType);
|
||||
}
|
||||
else {
|
||||
usageContext.isNumber = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.PlusEqualsToken:
|
||||
case SyntaxKind.PlusToken:
|
||||
const otherOperandType = checker.getTypeAtLocation(parent.left === node ? parent.right : parent.left);
|
||||
if (otherOperandType.flags & TypeFlags.EnumLike) {
|
||||
addCandidateType(usageContext, otherOperandType);
|
||||
}
|
||||
else if (otherOperandType.flags & TypeFlags.NumberLike) {
|
||||
usageContext.isNumber = true;
|
||||
}
|
||||
else if (otherOperandType.flags & TypeFlags.StringLike) {
|
||||
usageContext.isString = true;
|
||||
}
|
||||
else {
|
||||
usageContext.isNumberOrString = true;
|
||||
}
|
||||
break;
|
||||
|
||||
// AssignmentOperators
|
||||
case SyntaxKind.EqualsToken:
|
||||
case SyntaxKind.EqualsEqualsToken:
|
||||
case SyntaxKind.EqualsEqualsEqualsToken:
|
||||
case SyntaxKind.ExclamationEqualsEqualsToken:
|
||||
case SyntaxKind.ExclamationEqualsToken:
|
||||
addCandidateType(usageContext, checker.getTypeAtLocation(parent.left === node ? parent.right : parent.left));
|
||||
break;
|
||||
|
||||
case SyntaxKind.InKeyword:
|
||||
if (node === parent.left) {
|
||||
usageContext.isString = true;
|
||||
}
|
||||
break;
|
||||
|
||||
// LogicalOperator
|
||||
case SyntaxKind.BarBarToken:
|
||||
if (node === parent.left &&
|
||||
(node.parent.parent.kind === SyntaxKind.VariableDeclaration || isAssignmentExpression(node.parent.parent, /*excludeCompoundAssignment*/ true))) {
|
||||
// var x = x || {};
|
||||
// TODO: use getFalsyflagsOfType
|
||||
addCandidateType(usageContext, checker.getTypeAtLocation(parent.right));
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.AmpersandAmpersandToken:
|
||||
case SyntaxKind.CommaToken:
|
||||
case SyntaxKind.InstanceOfKeyword:
|
||||
// nothing to infer here
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function inferTypeFromSwitchStatementLabelContext(parent: CaseOrDefaultClause, checker: TypeChecker, usageContext: UsageContext): void {
|
||||
addCandidateType(usageContext, checker.getTypeAtLocation((<SwitchStatement>parent.parent.parent).expression));
|
||||
}
|
||||
|
||||
function inferTypeFromCallExpressionContext(parent: CallExpression | NewExpression, checker: TypeChecker, usageContext: UsageContext): void {
|
||||
const callContext: CallContext = {
|
||||
argumentTypes: [],
|
||||
returnType: {}
|
||||
};
|
||||
|
||||
if (parent.arguments) {
|
||||
for (const argument of parent.arguments) {
|
||||
callContext.argumentTypes.push(checker.getTypeAtLocation(argument));
|
||||
}
|
||||
}
|
||||
|
||||
inferTypeFromContext(parent, checker, callContext.returnType);
|
||||
if (parent.kind === SyntaxKind.CallExpression) {
|
||||
(usageContext.callContexts || (usageContext.callContexts = [])).push(callContext);
|
||||
}
|
||||
else {
|
||||
(usageContext.constructContexts || (usageContext.constructContexts = [])).push(callContext);
|
||||
}
|
||||
}
|
||||
|
||||
function inferTypeFromPropertyAccessExpressionContext(parent: PropertyAccessExpression, checker: TypeChecker, usageContext: UsageContext): void {
|
||||
const name = escapeLeadingUnderscores(parent.name.text);
|
||||
if (!usageContext.properties) {
|
||||
usageContext.properties = createUnderscoreEscapedMap<UsageContext>();
|
||||
}
|
||||
const propertyUsageContext = {};
|
||||
inferTypeFromContext(parent, checker, propertyUsageContext);
|
||||
usageContext.properties.set(name, propertyUsageContext);
|
||||
}
|
||||
|
||||
function inferTypeFromPropertyElementExpressionContext(parent: ElementAccessExpression, node: Expression, checker: TypeChecker, usageContext: UsageContext): void {
|
||||
if (node === parent.argumentExpression) {
|
||||
usageContext.isNumberOrString = true;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
const indexType = checker.getTypeAtLocation(parent);
|
||||
const indexUsageContext = {};
|
||||
inferTypeFromContext(parent, checker, indexUsageContext);
|
||||
if (indexType.flags & TypeFlags.NumberLike) {
|
||||
usageContext.numberIndexContext = indexUsageContext;
|
||||
}
|
||||
else {
|
||||
usageContext.stringIndexContext = indexUsageContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeFromUsageContext(usageContext: UsageContext, checker: TypeChecker): Type | undefined {
|
||||
if (usageContext.isNumberOrString && !usageContext.isNumber && !usageContext.isString) {
|
||||
return checker.getUnionType([checker.getNumberType(), checker.getStringType()]);
|
||||
}
|
||||
else if (usageContext.isNumber) {
|
||||
return checker.getNumberType();
|
||||
}
|
||||
else if (usageContext.isString) {
|
||||
return checker.getStringType();
|
||||
}
|
||||
else if (usageContext.candidateTypes) {
|
||||
return checker.getWidenedType(checker.getUnionType(map(usageContext.candidateTypes, t => checker.getBaseTypeOfLiteralType(t)), /*subtypeReduction*/ true));
|
||||
}
|
||||
else if (usageContext.properties && hasCallContext(usageContext.properties.get("then" as __String))) {
|
||||
const paramType = getParameterTypeFromCallContexts(0, usageContext.properties.get("then" as __String).callContexts, /*isRestParameter*/ false, checker);
|
||||
const types = paramType.getCallSignatures().map(c => c.getReturnType());
|
||||
return checker.createPromiseType(types.length ? checker.getUnionType(types, /*subtypeReduction*/ true) : checker.getAnyType());
|
||||
}
|
||||
else if (usageContext.properties && hasCallContext(usageContext.properties.get("push" as __String))) {
|
||||
return checker.createArrayType(getParameterTypeFromCallContexts(0, usageContext.properties.get("push" as __String).callContexts, /*isRestParameter*/ false, checker));
|
||||
}
|
||||
else if (usageContext.properties || usageContext.callContexts || usageContext.constructContexts || usageContext.numberIndexContext || usageContext.stringIndexContext) {
|
||||
const members = createUnderscoreEscapedMap<Symbol>();
|
||||
const callSignatures: Signature[] = [];
|
||||
const constructSignatures: Signature[] = [];
|
||||
let stringIndexInfo: IndexInfo;
|
||||
let numberIndexInfo: IndexInfo;
|
||||
|
||||
if (usageContext.properties) {
|
||||
usageContext.properties.forEach((context, name) => {
|
||||
const symbol = checker.createSymbol(SymbolFlags.Property, name);
|
||||
symbol.type = getTypeFromUsageContext(context, checker);
|
||||
members.set(name, symbol);
|
||||
});
|
||||
}
|
||||
|
||||
if (usageContext.callContexts) {
|
||||
for (const callContext of usageContext.callContexts) {
|
||||
callSignatures.push(getSignatureFromCallContext(callContext, checker));
|
||||
}
|
||||
}
|
||||
|
||||
if (usageContext.constructContexts) {
|
||||
for (const constructContext of usageContext.constructContexts) {
|
||||
constructSignatures.push(getSignatureFromCallContext(constructContext, checker));
|
||||
}
|
||||
}
|
||||
|
||||
if (usageContext.numberIndexContext) {
|
||||
numberIndexInfo = checker.createIndexInfo(getTypeFromUsageContext(usageContext.numberIndexContext, checker), /*isReadonly*/ false);
|
||||
}
|
||||
|
||||
if (usageContext.stringIndexContext) {
|
||||
stringIndexInfo = checker.createIndexInfo(getTypeFromUsageContext(usageContext.stringIndexContext, checker), /*isReadonly*/ false);
|
||||
}
|
||||
|
||||
return checker.createAnonymousType(/*symbol*/ undefined, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getParameterTypeFromCallContexts(parameterIndex: number, callContexts: CallContext[], isRestParameter: boolean, checker: TypeChecker) {
|
||||
let types: Type[] = [];
|
||||
if (callContexts) {
|
||||
for (const callContext of callContexts) {
|
||||
if (callContext.argumentTypes.length > parameterIndex) {
|
||||
if (isRestParameter) {
|
||||
types = concatenate(types, map(callContext.argumentTypes.slice(parameterIndex), a => checker.getBaseTypeOfLiteralType(a)));
|
||||
}
|
||||
else {
|
||||
types.push(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[parameterIndex]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (types.length) {
|
||||
const type = checker.getWidenedType(checker.getUnionType(types, /*subtypeReduction*/ true));
|
||||
return isRestParameter ? checker.createArrayType(type) : type;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getSignatureFromCallContext(callContext: CallContext, checker: TypeChecker): Signature {
|
||||
const parameters: Symbol[] = [];
|
||||
for (let i = 0; i < callContext.argumentTypes.length; i++) {
|
||||
const symbol = checker.createSymbol(SymbolFlags.FunctionScopedVariable, escapeLeadingUnderscores(`arg${i}`));
|
||||
symbol.type = checker.getWidenedType(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[i]));
|
||||
parameters.push(symbol);
|
||||
}
|
||||
const returnType = getTypeFromUsageContext(callContext.returnType, checker);
|
||||
return checker.createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, callContext.argumentTypes.length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
|
||||
}
|
||||
|
||||
function addCandidateType(context: UsageContext, type: Type) {
|
||||
if (type && !(type.flags & TypeFlags.Any) && !(type.flags & TypeFlags.Never)) {
|
||||
(context.candidateTypes || (context.candidateTypes = [])).push(type);
|
||||
}
|
||||
}
|
||||
|
||||
function hasCallContext(usageContext: UsageContext) {
|
||||
return usageContext && usageContext.callContexts;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3068,6 +3068,8 @@ declare namespace ts {
|
|||
function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause;
|
||||
/** True if node is of a kind that may contain comment text. */
|
||||
function isJSDocCommentContainingNode(node: Node): boolean;
|
||||
function isSetAccessor(node: Node): node is SetAccessorDeclaration;
|
||||
function isGetAccessor(node: Node): node is GetAccessorDeclaration;
|
||||
}
|
||||
declare namespace ts {
|
||||
interface ErrorCallback {
|
||||
|
|
|
@ -3123,6 +3123,8 @@ declare namespace ts {
|
|||
function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause;
|
||||
/** True if node is of a kind that may contain comment text. */
|
||||
function isJSDocCommentContainingNode(node: Node): boolean;
|
||||
function isSetAccessor(node: Node): node is SetAccessorDeclaration;
|
||||
function isGetAccessor(node: Node): node is GetAccessorDeclaration;
|
||||
}
|
||||
declare namespace ts {
|
||||
function createNode(kind: SyntaxKind, pos?: number, end?: number): Node;
|
||||
|
|
9
tests/cases/fourslash/codeFixInferFromUsage.ts
Normal file
9
tests/cases/fourslash/codeFixInferFromUsage.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////[|var foo;|]
|
||||
////function f() {
|
||||
//// foo += 2;
|
||||
////}
|
||||
|
||||
verify.rangeAfterCodeFix("var foo: number;",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0);
|
10
tests/cases/fourslash/codeFixInferFromUsageGetter.ts
Normal file
10
tests/cases/fourslash/codeFixInferFromUsageGetter.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////declare class C {
|
||||
//// [|get x();|]
|
||||
////}
|
||||
////}
|
||||
////(new C).x = 1;
|
||||
|
||||
verify.rangeAfterCodeFix("get x(): number;", undefined, undefined, 0);
|
11
tests/cases/fourslash/codeFixInferFromUsageGetter2.ts
Normal file
11
tests/cases/fourslash/codeFixInferFromUsageGetter2.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////class C {
|
||||
//// [|get x() |]{
|
||||
//// return undefined;
|
||||
//// }
|
||||
////}
|
||||
////(new C).x = 1;
|
||||
|
||||
verify.rangeAfterCodeFix("get x(): number", undefined, undefined, 0);
|
|
@ -0,0 +1,20 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////function f1([|a |]) { }
|
||||
////function h1() {
|
||||
//// class C { p: number };
|
||||
//// f1({ ofTypeC: new C() });
|
||||
////}
|
||||
////
|
||||
////function f2([|a |]) { }
|
||||
////function h2() {
|
||||
//// interface I { a: number }
|
||||
//// var i: I = {a : 1};
|
||||
//// f2(i);
|
||||
//// f2(2);
|
||||
//// f2(false);
|
||||
////}
|
||||
////
|
||||
|
||||
verify.not.codeFixAvailable();
|
11
tests/cases/fourslash/codeFixInferFromUsageMember.ts
Normal file
11
tests/cases/fourslash/codeFixInferFromUsageMember.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////class C {
|
||||
//// [|p;|]
|
||||
//// method() {
|
||||
//// this.p.push(10);
|
||||
//// }
|
||||
////}
|
||||
|
||||
verify.rangeAfterCodeFix("p: number[];");
|
10
tests/cases/fourslash/codeFixInferFromUsageMember2.ts
Normal file
10
tests/cases/fourslash/codeFixInferFromUsageMember2.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////interface I {
|
||||
//// [|p;|]
|
||||
////}
|
||||
////var i: I;
|
||||
////i.p = 0;
|
||||
|
||||
verify.rangeAfterCodeFix("p: number;");
|
9
tests/cases/fourslash/codeFixInferFromUsageMember3.ts
Normal file
9
tests/cases/fourslash/codeFixInferFromUsageMember3.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////class C {
|
||||
//// constructor([|public p)|] { }
|
||||
////}
|
||||
////new C("string");
|
||||
|
||||
verify.rangeAfterCodeFix("public p: string)");
|
|
@ -0,0 +1,9 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
//// function f([|a, b, c, d: number, e = 0, ...d |]) {
|
||||
//// }
|
||||
//// f(1, "string", { a: 1 }, {shouldNotBeHere: 2}, {shouldNotBeHere: 2}, 3, "string");
|
||||
|
||||
|
||||
verify.rangeAfterCodeFix("a: number, b: string, c: { a: number; }, d: number, e = 0, ...d: (string | number)[]", /*includeWhiteSpace*/ false, /*errorCode*/ undefined, /*index*/ 1);
|
|
@ -0,0 +1,9 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////function f([|a? |]){
|
||||
////}
|
||||
////f();
|
||||
////f(1);
|
||||
|
||||
verify.rangeAfterCodeFix("a?: number");
|
|
@ -0,0 +1,8 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////function f([|a? |]){
|
||||
//// if (a < 9) return;
|
||||
////}
|
||||
|
||||
verify.rangeAfterCodeFix("a?: number");
|
11
tests/cases/fourslash/codeFixInferFromUsageRestParam.ts
Normal file
11
tests/cases/fourslash/codeFixInferFromUsageRestParam.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////function f(a: number, [|...rest |]){
|
||||
////}
|
||||
////f(1);
|
||||
////f(2, "s1");
|
||||
////f(3, "s1", "s2");
|
||||
////f(3, "s1", "s2", "s3", "s4");
|
||||
|
||||
verify.rangeAfterCodeFix("...rest: string[]");
|
11
tests/cases/fourslash/codeFixInferFromUsageRestParam2.ts
Normal file
11
tests/cases/fourslash/codeFixInferFromUsageRestParam2.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////function f(a: number, [|...rest |]){
|
||||
////}
|
||||
////f(1);
|
||||
////f(2, "s1");
|
||||
////f(3, false, "s2");
|
||||
////f(4, "s1", "s2", false, "s4");
|
||||
|
||||
verify.rangeAfterCodeFix("...rest: (string | boolean)[]");
|
8
tests/cases/fourslash/codeFixInferFromUsageRestParam3.ts
Normal file
8
tests/cases/fourslash/codeFixInferFromUsageRestParam3.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////function f(a: number, [|...rest |]){
|
||||
//// rest.push(22);
|
||||
////}
|
||||
|
||||
verify.rangeAfterCodeFix("...rest: number[]");
|
10
tests/cases/fourslash/codeFixInferFromUsageSetter.ts
Normal file
10
tests/cases/fourslash/codeFixInferFromUsageSetter.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////class C {
|
||||
//// set [|x(v)|] {
|
||||
//// }
|
||||
////}
|
||||
////(new C).x = 1;
|
||||
|
||||
verify.rangeAfterCodeFix("x(v: number)", undefined, undefined, 0);
|
10
tests/cases/fourslash/codeFixInferFromUsageSetter2.ts
Normal file
10
tests/cases/fourslash/codeFixInferFromUsageSetter2.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////class C {
|
||||
//// set [|x(v)|] {
|
||||
//// }
|
||||
////}
|
||||
////(new C).x = 1;
|
||||
|
||||
verify.rangeAfterCodeFix("x(v: number)", undefined, undefined, 1);
|
9
tests/cases/fourslash/codeFixInferFromUsageVariable.ts
Normal file
9
tests/cases/fourslash/codeFixInferFromUsageVariable.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////[|var x;|]
|
||||
////function f() {
|
||||
//// x++;
|
||||
////}
|
||||
|
||||
verify.rangeAfterCodeFix("var x: number;", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0);
|
13
tests/cases/fourslash/codeFixInferFromUsageVariable2.ts
Normal file
13
tests/cases/fourslash/codeFixInferFromUsageVariable2.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noImplicitAny: true
|
||||
////[|var x;
|
||||
////function f() {
|
||||
//// x++;
|
||||
////}|]
|
||||
|
||||
verify.rangeAfterCodeFix(`var x: number;
|
||||
function f() {
|
||||
x++;
|
||||
}
|
||||
`, /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 1);
|
Loading…
Reference in a new issue