Merge pull request #33263 from microsoft/infer-from-usage/similarity-to-builtins

Infer from usage by similarity to builtin types
This commit is contained in:
Nathan Shively-Sanders 2019-09-24 07:44:34 -07:00 committed by GitHub
commit f1104806d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 322 additions and 107 deletions

View file

@ -523,6 +523,9 @@ namespace ts {
}, },
getApparentType, getApparentType,
getUnionType, getUnionType,
isTypeAssignableTo: (source, target) => {
return isTypeAssignableTo(source, target);
},
createAnonymousType, createAnonymousType,
createSignature, createSignature,
createSymbol, createSymbol,

View file

@ -3315,6 +3315,7 @@ namespace ts {
/* @internal */ getElementTypeOfArrayType(arrayType: Type): Type | undefined; /* @internal */ getElementTypeOfArrayType(arrayType: Type): Type | undefined;
/* @internal */ createPromiseType(type: Type): Type; /* @internal */ createPromiseType(type: Type): Type;
/* @internal */ isTypeAssignableTo(source: Type, target: Type): boolean;
/* @internal */ createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo | undefined, numberIndexInfo: IndexInfo | undefined): Type; /* @internal */ createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo | undefined, numberIndexInfo: IndexInfo | undefined): Type;
/* @internal */ createSignature( /* @internal */ createSignature(
declaration: SignatureDeclaration, declaration: SignatureDeclaration,

View file

@ -393,6 +393,19 @@ namespace ts.codefix {
function inferTypeFromReferences(program: Program, references: readonly Identifier[], cancellationToken: CancellationToken) { function inferTypeFromReferences(program: Program, references: readonly Identifier[], cancellationToken: CancellationToken) {
const checker = program.getTypeChecker(); const checker = program.getTypeChecker();
const builtinConstructors: { [s: string]: (t: Type) => Type } = {
string: () => checker.getStringType(),
number: () => checker.getNumberType(),
Array: t => checker.createArrayType(t),
Promise: t => checker.createPromiseType(t),
};
const builtins = [
checker.getStringType(),
checker.getNumberType(),
checker.createArrayType(checker.getAnyType()),
checker.createPromiseType(checker.getAnyType()),
];
return { return {
single, single,
parameters, parameters,
@ -401,26 +414,74 @@ namespace ts.codefix {
interface CallUsage { interface CallUsage {
argumentTypes: Type[]; argumentTypes: Type[];
returnType: Usage; return_: Usage;
} }
interface Usage { interface Usage {
isNumber?: boolean; isNumber: boolean | undefined;
isString?: boolean; isString: boolean | undefined;
/** Used ambiguously, eg x + ___ or object[___]; results in string | number if no other evidence exists */ /** Used ambiguously, eg x + ___ or object[___]; results in string | number if no other evidence exists */
isNumberOrString?: boolean; isNumberOrString: boolean | undefined;
candidateTypes?: Type[]; candidateTypes: Type[] | undefined;
properties?: UnderscoreEscapedMap<Usage>; properties: UnderscoreEscapedMap<Usage> | undefined;
calls?: CallUsage[]; calls: CallUsage[] | undefined;
constructs?: CallUsage[]; constructs: CallUsage[] | undefined;
numberIndex?: Usage; numberIndex: Usage | undefined;
stringIndex?: Usage; stringIndex: Usage | undefined;
candidateThisTypes?: Type[]; candidateThisTypes: Type[] | undefined;
inferredTypes: Type[] | undefined;
}
function createEmptyUsage(): Usage {
return {
isNumber: undefined,
isString: undefined,
isNumberOrString: undefined,
candidateTypes: undefined,
properties: undefined,
calls: undefined,
constructs: undefined,
numberIndex: undefined,
stringIndex: undefined,
candidateThisTypes: undefined,
inferredTypes: undefined,
};
}
function combineUsages(usages: Usage[]): Usage {
const combinedProperties = createUnderscoreEscapedMap<Usage[]>();
for (const u of usages) {
if (u.properties) {
u.properties.forEach((p, name) => {
if (!combinedProperties.has(name)) {
combinedProperties.set(name, []);
}
combinedProperties.get(name)!.push(p);
});
}
}
const properties = createUnderscoreEscapedMap<Usage>();
combinedProperties.forEach((ps, name) => {
properties.set(name, combineUsages(ps));
});
return {
isNumber: usages.some(u => u.isNumber),
isString: usages.some(u => u.isString),
isNumberOrString: usages.some(u => u.isNumberOrString),
candidateTypes: flatMap(usages, u => u.candidateTypes) as Type[],
properties,
calls: flatMap(usages, u => u.calls) as CallUsage[],
constructs: flatMap(usages, u => u.constructs) as CallUsage[],
numberIndex: forEach(usages, u => u.numberIndex),
stringIndex: forEach(usages, u => u.stringIndex),
candidateThisTypes: flatMap(usages, u => u.candidateThisTypes) as Type[],
inferredTypes: undefined, // clear type cache
};
} }
function single(): Type { function single(): Type {
return unifyFromUsage(inferTypesFromReferencesSingle(references)); return combineTypes(inferTypesFromReferencesSingle(references));
} }
function parameters(declaration: FunctionLike): ParameterInference[] | undefined { function parameters(declaration: FunctionLike): ParameterInference[] | undefined {
@ -428,7 +489,7 @@ namespace ts.codefix {
return undefined; return undefined;
} }
const usage: Usage = {}; const usage = createEmptyUsage();
for (const reference of references) { for (const reference of references) {
cancellationToken.throwIfCancellationRequested(); cancellationToken.throwIfCancellationRequested();
calculateUsageOfNode(reference, usage); calculateUsageOfNode(reference, usage);
@ -456,7 +517,7 @@ namespace ts.codefix {
const inferred = inferTypesFromReferencesSingle(getReferences(parameter.name, program, cancellationToken)); const inferred = inferTypesFromReferencesSingle(getReferences(parameter.name, program, cancellationToken));
types.push(...(isRest ? mapDefined(inferred, checker.getElementTypeOfArrayType) : inferred)); types.push(...(isRest ? mapDefined(inferred, checker.getElementTypeOfArrayType) : inferred));
} }
const type = unifyFromUsage(types); const type = combineTypes(types);
return { return {
type: isRest ? checker.createArrayType(type) : type, type: isRest ? checker.createArrayType(type) : type,
isOptional: isOptional && !isRest, isOptional: isOptional && !isRest,
@ -466,22 +527,22 @@ namespace ts.codefix {
} }
function thisParameter() { function thisParameter() {
const usage: Usage = {}; const usage = createEmptyUsage();
for (const reference of references) { for (const reference of references) {
cancellationToken.throwIfCancellationRequested(); cancellationToken.throwIfCancellationRequested();
calculateUsageOfNode(reference, usage); calculateUsageOfNode(reference, usage);
} }
return unifyFromUsage(usage.candidateThisTypes || emptyArray); return combineTypes(usage.candidateThisTypes || emptyArray);
} }
function inferTypesFromReferencesSingle(references: readonly Identifier[]): Type[] { function inferTypesFromReferencesSingle(references: readonly Identifier[]): Type[] {
const usage: Usage = {}; const usage: Usage = createEmptyUsage();
for (const reference of references) { for (const reference of references) {
cancellationToken.throwIfCancellationRequested(); cancellationToken.throwIfCancellationRequested();
calculateUsageOfNode(reference, usage); calculateUsageOfNode(reference, usage);
} }
return inferFromUsage(usage); return inferTypes(usage);
} }
function calculateUsageOfNode(node: Expression, usage: Usage): void { function calculateUsageOfNode(node: Expression, usage: Usage): void {
@ -490,6 +551,9 @@ namespace ts.codefix {
} }
switch (node.parent.kind) { switch (node.parent.kind) {
case SyntaxKind.ExpressionStatement:
addCandidateType(usage, checker.getVoidType());
break;
case SyntaxKind.PostfixUnaryExpression: case SyntaxKind.PostfixUnaryExpression:
usage.isNumber = true; usage.isNumber = true;
break; break;
@ -632,6 +696,9 @@ namespace ts.codefix {
else if (otherOperandType.flags & TypeFlags.StringLike) { else if (otherOperandType.flags & TypeFlags.StringLike) {
usage.isString = true; usage.isString = true;
} }
else if (otherOperandType.flags & TypeFlags.Any) {
// do nothing, maybe we'll learn something elsewhere
}
else { else {
usage.isNumberOrString = true; usage.isNumberOrString = true;
} }
@ -677,7 +744,7 @@ namespace ts.codefix {
function inferTypeFromCallExpression(parent: CallExpression | NewExpression, usage: Usage): void { function inferTypeFromCallExpression(parent: CallExpression | NewExpression, usage: Usage): void {
const call: CallUsage = { const call: CallUsage = {
argumentTypes: [], argumentTypes: [],
returnType: {} return_: createEmptyUsage()
}; };
if (parent.arguments) { if (parent.arguments) {
@ -686,7 +753,7 @@ namespace ts.codefix {
} }
} }
calculateUsageOfNode(parent, call.returnType); calculateUsageOfNode(parent, call.return_);
if (parent.kind === SyntaxKind.CallExpression) { if (parent.kind === SyntaxKind.CallExpression) {
(usage.calls || (usage.calls = [])).push(call); (usage.calls || (usage.calls = [])).push(call);
} }
@ -700,7 +767,7 @@ namespace ts.codefix {
if (!usage.properties) { if (!usage.properties) {
usage.properties = createUnderscoreEscapedMap<Usage>(); usage.properties = createUnderscoreEscapedMap<Usage>();
} }
const propertyUsage = usage.properties.get(name) || { }; const propertyUsage = usage.properties.get(name) || createEmptyUsage();
calculateUsageOfNode(parent, propertyUsage); calculateUsageOfNode(parent, propertyUsage);
usage.properties.set(name, propertyUsage); usage.properties.set(name, propertyUsage);
} }
@ -712,7 +779,7 @@ namespace ts.codefix {
} }
else { else {
const indexType = checker.getTypeAtLocation(parent.argumentExpression); const indexType = checker.getTypeAtLocation(parent.argumentExpression);
const indexUsage = {}; const indexUsage = createEmptyUsage();
calculateUsageOfNode(parent, indexUsage); calculateUsageOfNode(parent, indexUsage);
if (indexType.flags & TypeFlags.NumberLike) { if (indexType.flags & TypeFlags.NumberLike) {
usage.numberIndex = indexUsage; usage.numberIndex = indexUsage;
@ -752,8 +819,12 @@ namespace ts.codefix {
return inferences.filter(i => toRemove.every(f => !f(i))); return inferences.filter(i => toRemove.every(f => !f(i)));
} }
function unifyFromUsage(inferences: readonly Type[], fallback = checker.getAnyType()): Type { function combineFromUsage(usage: Usage) {
if (!inferences.length) return fallback; return combineTypes(inferTypes(usage));
}
function combineTypes(inferences: readonly Type[]): Type {
if (!inferences.length) return checker.getAnyType();
// 1. string or number individually override string | number // 1. string or number individually override string | number
// 2. non-any, non-void overrides any or void // 2. non-any, non-void overrides any or void
@ -776,12 +847,12 @@ namespace ts.codefix {
const anons = good.filter(i => checker.getObjectFlags(i) & ObjectFlags.Anonymous) as AnonymousType[]; const anons = good.filter(i => checker.getObjectFlags(i) & ObjectFlags.Anonymous) as AnonymousType[];
if (anons.length) { if (anons.length) {
good = good.filter(i => !(checker.getObjectFlags(i) & ObjectFlags.Anonymous)); good = good.filter(i => !(checker.getObjectFlags(i) & ObjectFlags.Anonymous));
good.push(unifyAnonymousTypes(anons)); good.push(combineAnonymousTypes(anons));
} }
return checker.getWidenedType(checker.getUnionType(good)); return checker.getWidenedType(checker.getUnionType(good.map(checker.getBaseTypeOfLiteralType), UnionReduction.Subtype));
} }
function unifyAnonymousTypes(anons: AnonymousType[]) { function combineAnonymousTypes(anons: AnonymousType[]) {
if (anons.length === 1) { if (anons.length === 1) {
return anons[0]; return anons[0];
} }
@ -822,7 +893,7 @@ namespace ts.codefix {
numberIndices.length ? checker.createIndexInfo(checker.getUnionType(numberIndices), numberIndexReadonly) : undefined); numberIndices.length ? checker.createIndexInfo(checker.getUnionType(numberIndices), numberIndexReadonly) : undefined);
} }
function inferFromUsage(usage: Usage) { function inferTypes(usage: Usage): Type[] {
const types = []; const types = [];
if (usage.isNumber) { if (usage.isNumber) {
@ -834,92 +905,155 @@ namespace ts.codefix {
if (usage.isNumberOrString) { if (usage.isNumberOrString) {
types.push(checker.getUnionType([checker.getStringType(), checker.getNumberType()])); types.push(checker.getUnionType([checker.getStringType(), checker.getNumberType()]));
} }
if (usage.numberIndex) {
types.push(checker.createArrayType(combineFromUsage(usage.numberIndex)));
}
if (usage.properties && usage.properties.size
|| usage.calls && usage.calls.length
|| usage.constructs && usage.constructs.length
|| usage.stringIndex) {
types.push(inferStructuralType(usage));
}
types.push(...(usage.candidateTypes || []).map(t => checker.getBaseTypeOfLiteralType(t))); types.push(...(usage.candidateTypes || []).map(t => checker.getBaseTypeOfLiteralType(t)));
types.push(...inferNamedTypesFromProperties(usage));
if (usage.properties && hasCalls(usage.properties.get("then" as __String))) {
const paramType = getParameterTypeFromCalls(0, usage.properties.get("then" as __String)!.calls!, /*isRestParameter*/ false)!; // TODO: GH#18217
const types = paramType.getCallSignatures().map(sig => sig.getReturnType());
types.push(checker.createPromiseType(types.length ? checker.getUnionType(types, UnionReduction.Subtype) : checker.getAnyType()));
}
else if (usage.properties && hasCalls(usage.properties.get("push" as __String))) {
types.push(checker.createArrayType(getParameterTypeFromCalls(0, usage.properties.get("push" as __String)!.calls!, /*isRestParameter*/ false)!));
}
if (usage.numberIndex) {
types.push(checker.createArrayType(recur(usage.numberIndex)));
}
else if (usage.properties || usage.calls || usage.constructs || usage.stringIndex) {
const members = createUnderscoreEscapedMap<Symbol>();
const callSignatures: Signature[] = [];
const constructSignatures: Signature[] = [];
let stringIndexInfo: IndexInfo | undefined;
if (usage.properties) {
usage.properties.forEach((u, name) => {
const symbol = checker.createSymbol(SymbolFlags.Property, name);
symbol.type = recur(u);
members.set(name, symbol);
});
}
if (usage.calls) {
for (const call of usage.calls) {
callSignatures.push(getSignatureFromCall(call));
}
}
if (usage.constructs) {
for (const construct of usage.constructs) {
constructSignatures.push(getSignatureFromCall(construct));
}
}
if (usage.stringIndex) {
stringIndexInfo = checker.createIndexInfo(recur(usage.stringIndex), /*isReadonly*/ false);
}
types.push(checker.createAnonymousType(/*symbol*/ undefined!, members, callSignatures, constructSignatures, stringIndexInfo, /*numberIndexInfo*/ undefined)); // TODO: GH#18217
}
return types; return types;
function recur(innerUsage: Usage): Type {
return unifyFromUsage(inferFromUsage(innerUsage));
}
} }
function getParameterTypeFromCalls(parameterIndex: number, calls: CallUsage[], isRestParameter: boolean) { function inferStructuralType(usage: Usage) {
let types: Type[] = []; const members = createUnderscoreEscapedMap<Symbol>();
if (calls) { if (usage.properties) {
for (const call of calls) { usage.properties.forEach((u, name) => {
if (call.argumentTypes.length > parameterIndex) { const symbol = checker.createSymbol(SymbolFlags.Property, name);
if (isRestParameter) { symbol.type = combineFromUsage(u);
types = concatenate(types, map(call.argumentTypes.slice(parameterIndex), a => checker.getBaseTypeOfLiteralType(a))); members.set(name, symbol);
} });
else { }
types.push(checker.getBaseTypeOfLiteralType(call.argumentTypes[parameterIndex])); const callSignatures: Signature[] = usage.calls ? [getSignatureFromCalls(usage.calls)] : [];
const constructSignatures: Signature[] = usage.constructs ? [getSignatureFromCalls(usage.constructs)] : [];
const stringIndexInfo = usage.stringIndex && checker.createIndexInfo(combineFromUsage(usage.stringIndex), /*isReadonly*/ false);
return checker.createAnonymousType(/*symbol*/ undefined!, members, callSignatures, constructSignatures, stringIndexInfo, /*numberIndexInfo*/ undefined); // TODO: GH#18217
}
function inferNamedTypesFromProperties(usage: Usage): Type[] {
if (!usage.properties || !usage.properties.size) return [];
const types = builtins.filter(t => allPropertiesAreAssignableToUsage(t, usage));
if (0 < types.length && types.length < 3) {
return types.map(t => inferInstantiationFromUsage(t, usage));
}
return [];
}
function allPropertiesAreAssignableToUsage(type: Type, usage: Usage) {
if (!usage.properties) return false;
return !forEachEntry(usage.properties, (propUsage, name) => {
const source = checker.getTypeOfPropertyOfType(type, name as string);
if (!source) {
return true;
}
if (propUsage.calls) {
const sigs = checker.getSignaturesOfType(source, SignatureKind.Call);
return !sigs.length || !checker.isTypeAssignableTo(source, getFunctionFromCalls(propUsage.calls));
}
else {
return !checker.isTypeAssignableTo(source, combineFromUsage(propUsage));
}
});
}
/**
* inference is limited to
* 1. generic types with a single parameter
* 2. inference to/from calls with a single signature
*/
function inferInstantiationFromUsage(type: Type, usage: Usage) {
if (!(getObjectFlags(type) & ObjectFlags.Reference) || !usage.properties) {
return type;
}
const generic = (type as TypeReference).target;
const singleTypeParameter = singleOrUndefined(generic.typeParameters);
if (!singleTypeParameter) return type;
const types: Type[] = [];
usage.properties.forEach((propUsage, name) => {
const genericPropertyType = checker.getTypeOfPropertyOfType(generic, name as string);
Debug.assert(!!genericPropertyType, "generic should have all the properties of its reference.");
types.push(...inferTypeParameters(genericPropertyType!, combineFromUsage(propUsage), singleTypeParameter));
});
return builtinConstructors[type.symbol.escapedName as string](combineTypes(types));
}
function inferTypeParameters(genericType: Type, usageType: Type, typeParameter: Type): readonly Type[] {
if (genericType === typeParameter) {
return [usageType];
}
else if (genericType.flags & TypeFlags.UnionOrIntersection) {
return flatMap((genericType as UnionOrIntersectionType).types, t => inferTypeParameters(t, usageType, typeParameter));
}
else if (getObjectFlags(genericType) & ObjectFlags.Reference && getObjectFlags(usageType) & ObjectFlags.Reference) {
// this is wrong because we need a reference to the targetType to, so we can check that it's also a reference
const genericArgs = (genericType as TypeReference).typeArguments;
const usageArgs = (usageType as TypeReference).typeArguments;
const types = [];
if (genericArgs && usageArgs) {
for (let i = 0; i < genericArgs.length; i++) {
if (usageArgs[i]) {
types.push(...inferTypeParameters(genericArgs[i], usageArgs[i], typeParameter));
} }
} }
} }
return types;
} }
const genericSigs = checker.getSignaturesOfType(genericType, SignatureKind.Call);
if (types.length) { const usageSigs = checker.getSignaturesOfType(usageType, SignatureKind.Call);
const type = checker.getWidenedType(checker.getUnionType(types, UnionReduction.Subtype)); if (genericSigs.length === 1 && usageSigs.length === 1) {
return isRestParameter ? checker.createArrayType(type) : type; return inferFromSignatures(genericSigs[0], usageSigs[0], typeParameter);
} }
return undefined; return [];
} }
function getSignatureFromCall(call: CallUsage): Signature { function inferFromSignatures(genericSig: Signature, usageSig: Signature, typeParameter: Type) {
const types = [];
for (let i = 0; i < genericSig.parameters.length; i++) {
const genericParam = genericSig.parameters[i];
const usageParam = usageSig.parameters[i];
const isRest = genericSig.declaration && isRestParameter(genericSig.declaration.parameters[i]);
if (!usageParam) {
break;
}
let genericParamType = checker.getTypeOfSymbolAtLocation(genericParam, genericParam.valueDeclaration);
const elementType = isRest && checker.getElementTypeOfArrayType(genericParamType);
if (elementType) {
genericParamType = elementType;
}
const targetType = (usageParam as SymbolLinks).type || checker.getTypeOfSymbolAtLocation(usageParam, usageParam.valueDeclaration);
types.push(...inferTypeParameters(genericParamType, targetType, typeParameter));
}
const genericReturn = checker.getReturnTypeOfSignature(genericSig);
const usageReturn = checker.getReturnTypeOfSignature(usageSig);
types.push(...inferTypeParameters(genericReturn, usageReturn, typeParameter));
return types;
}
function getFunctionFromCalls(calls: CallUsage[]) {
return checker.createAnonymousType(undefined!, createSymbolTable(), [getSignatureFromCalls(calls)], emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined);
}
function getSignatureFromCalls(calls: CallUsage[]): Signature {
const parameters: Symbol[] = []; const parameters: Symbol[] = [];
for (let i = 0; i < call.argumentTypes.length; i++) { const length = Math.max(...calls.map(c => c.argumentTypes.length));
for (let i = 0; i < length; i++) {
const symbol = checker.createSymbol(SymbolFlags.FunctionScopedVariable, escapeLeadingUnderscores(`arg${i}`)); const symbol = checker.createSymbol(SymbolFlags.FunctionScopedVariable, escapeLeadingUnderscores(`arg${i}`));
symbol.type = checker.getWidenedType(checker.getBaseTypeOfLiteralType(call.argumentTypes[i])); symbol.type = combineTypes(calls.map(call => call.argumentTypes[i] || checker.getUndefinedType()));
if (calls.some(call => call.argumentTypes[i] === undefined)) {
symbol.flags |= SymbolFlags.Optional;
}
parameters.push(symbol); parameters.push(symbol);
} }
const returnType = unifyFromUsage(inferFromUsage(call.returnType), checker.getVoidType()); const returnType = combineFromUsage(combineUsages(calls.map(call => call.return_)));
// TODO: GH#18217 // TODO: GH#18217
return checker.createSignature(/*declaration*/ undefined!, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, call.argumentTypes.length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); return checker.createSignature(/*declaration*/ undefined!, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
} }
function addCandidateType(usage: Usage, type: Type | undefined) { function addCandidateType(usage: Usage, type: Type | undefined) {
@ -933,9 +1067,5 @@ namespace ts.codefix {
(usage.candidateThisTypes || (usage.candidateThisTypes = [])).push(type); (usage.candidateThisTypes || (usage.candidateThisTypes = [])).push(type);
} }
} }
function hasCalls(usage: Usage | undefined): boolean {
return !!usage && !!usage.calls;
}
} }
} }

View file

@ -0,0 +1,9 @@
/// <reference path='fourslash.ts' />
// @noImplicitAny: true
//// function inferAny( [| app |] ) {
//// const result = app.use('hi')
//// return result
//// }
verify.rangeAfterCodeFix("app: { use: (arg0: string) => any; }");

View file

@ -0,0 +1,8 @@
/// <reference path='fourslash.ts' />
// @noImplicitAny: true
//// function inferVoid( [| app |] ) {
//// app.use('hi')
//// }
verify.rangeAfterCodeFix("app: { use: (arg0: string) => void; }");

View file

@ -2,8 +2,8 @@
// @noImplicitAny: true // @noImplicitAny: true
////function wrap( [| arr |] ) { ////function wrap( [| arr |] ) {
//// arr.sort(function (a: number, b: number) { return a < b ? -1 : 1 }) //// arr.other(function (a: number, b: number) { return a < b ? -1 : 1 })
//// } //// }
// https://github.com/Microsoft/TypeScript/issues/29330 // https://github.com/Microsoft/TypeScript/issues/29330
verify.rangeAfterCodeFix("arr: { sort: (arg0: (a: number, b: number) => 1 | -1) => void; }"); verify.rangeAfterCodeFix("arr: { other: (arg0: (a: number, b: number) => 1 | -1) => void; }");

View file

@ -0,0 +1,10 @@
/// <reference path='fourslash.ts' />
// @noImplicitAny: true
//// function wrap( [| s |] ) {
//// return s.length + s.indexOf('hi')
//// }
// https://github.com/Microsoft/TypeScript/issues/29330
verify.rangeAfterCodeFix("s: string | string[]");

View file

@ -0,0 +1,9 @@
/// <reference path='fourslash.ts' />
// @noImplicitAny: true
//// function foo([|a, m |]) {
//// return a + m
//// }
verify.rangeAfterCodeFix("a: any, m: any", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0);

View file

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
// @noImplicitAny: true
//// function foo([|p, a, b, c, d, e |]) {
//// var x: string = a.pop()
//// b.reverse()
//// var rr: boolean[] = c.reverse()
//// d.some(t => t > 1); // can't infer from callbacks right now
//// var y = e.concat(12); // can't infer from overloaded functions right now
//// return p.push(12)
//// }
verify.rangeAfterCodeFix("p: number[], a: string[], b: any[], c: boolean[], d: any[], e: any[]", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0);

View file

@ -1,7 +1,7 @@
/// <reference path='fourslash.ts' /> /// <reference path='fourslash.ts' />
////class C { ////class C {
//// //// p = 2
////} ////}
////var c = new C() ////var c = new C()
////function f([|x, y |]) { ////function f([|x, y |]) {

View file

@ -16,7 +16,7 @@ verify.codeFix({
index: 0, index: 0,
newFileContent: newFileContent:
`/** `/**
* @param {(arg0: any) => void} callback * @param {(arg0: any) => any} callback
*/ */
function coll(callback /*, name1, name2, ... */) { function coll(callback /*, name1, name2, ... */) {
return callback(this); return callback(this);

View file

@ -30,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) => any; }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0);

View file

@ -0,0 +1,10 @@
/// <reference path='fourslash.ts' />
// @noImplicitAny: true
//// function foo([|a, m |]) {
//// a = 'hi'
//// m = 1
//// }
verify.rangeAfterCodeFix("a: string, m: number", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0);

View file

@ -0,0 +1,9 @@
/// <reference path='fourslash.ts' />
// @noImplicitAny: true
//// function foo([|p |]) {
//// return p.then((x: string[]) => x[0])
//// }
verify.rangeAfterCodeFix("p: Promise<string[]>", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0);

View file

@ -12,4 +12,4 @@
//// return x.y.z //// return x.y.z
////} ////}
verify.rangeAfterCodeFix("a: { b: { c: any; }; }, m: { n: () => number; }, x: { y: { z: number[]; }; }", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0); verify.rangeAfterCodeFix("a: { b: { c: void; }; }, m: { n: () => number; }, x: { y: { z: number[]; }; }", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0);

View file

@ -21,7 +21,7 @@ verify.codeFix({
index: 0, index: 0,
newFileContent: newFileContent:
`/** `/**
* @param {{ b: { c: any; }; }} a * @param {{ b: { c: void; }; }} a
* @param {{ n: () => number; }} m * @param {{ n: () => number; }} m
* @param {{ y: { z: number[]; }; }} x * @param {{ y: { z: number[]; }; }} x
*/ */

View file

@ -0,0 +1,12 @@
/// <reference path='fourslash.ts' />
// @noImplicitAny: true
//// function foo([|p, a, b |]) {
//// var x
//// p.charAt(x)
//// a.charAt(0)
//// b.concat('hi')
//// }
verify.rangeAfterCodeFix("p: string, a: string, b: string | any[]", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0);