In JS, constructor functions infer from call+construct (#28353)

* constructor functions infer from call+construct

Also fix an incorrect combining of inferences for rest parameters: the
inferred types will be arrays in the body of the function and the
arguments from outside the function will be the element type.

* All functions infer from call+construct contexts
This commit is contained in:
Nathan Shively-Sanders 2018-11-16 09:51:07 -08:00 committed by GitHub
parent 1089424035
commit ea8ccc2ce4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 67 additions and 6 deletions

View File

@ -283,6 +283,7 @@ namespace ts {
getNumberType: () => numberType,
createPromiseType,
createArrayType,
getElementTypeOfArrayType,
getBooleanType: () => booleanType,
getFalseType: (fresh?) => fresh ? falseType : regularFalseType,
getTrueType: (fresh?) => fresh ? trueType : regularTrueType,
@ -13222,6 +13223,10 @@ namespace ts {
return !!(getObjectFlags(type) & ObjectFlags.Reference) && (<TypeReference>type).target === globalReadonlyArrayType;
}
function getElementTypeOfArrayType(type: Type): Type | undefined {
return isArrayType(type) && (type as TypeReference).typeArguments ? (type as TypeReference).typeArguments![0] : undefined;
}
function isArrayLikeType(type: Type): boolean {
// A type is array-like if it is a reference to the global Array or global ReadonlyArray type,
// or if it is not the undefined or null type and if it is assignable to ReadonlyArray<any>

View File

@ -3141,6 +3141,7 @@ namespace ts {
/* @internal */ getNeverType(): Type;
/* @internal */ getUnionType(types: Type[], subtypeReduction?: UnionReduction): Type;
/* @internal */ createArrayType(elementType: Type): Type;
/* @internal */ getElementTypeOfArrayType(arrayType: Type): Type | undefined;
/* @internal */ createPromiseType(type: Type): Type;
/* @internal */ createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo | undefined, numberIndexInfo: IndexInfo | undefined): Type;

View File

@ -413,10 +413,9 @@ namespace ts.codefix {
cancellationToken.throwIfCancellationRequested();
inferTypeFromContext(reference, checker, usageContext);
}
const isConstructor = declaration.kind === SyntaxKind.Constructor;
const callContexts = isConstructor ? usageContext.constructContexts : usageContext.callContexts;
return callContexts && declaration.parameters.map((parameter, parameterIndex): ParameterInference => {
const types: Type[] = [];
const callContexts = [...usageContext.constructContexts || [], ...usageContext.callContexts || []];
return declaration.parameters.map((parameter, parameterIndex): ParameterInference => {
const types = [];
const isRest = isRestParameter(parameter);
let isOptional = false;
for (const callContext of callContexts) {
@ -434,7 +433,8 @@ namespace ts.codefix {
}
}
if (isIdentifier(parameter.name)) {
types.push(...inferTypesFromReferences(getReferences(parameter.name, program, cancellationToken), checker, cancellationToken));
const inferred = inferTypesFromReferences(getReferences(parameter.name, program, cancellationToken), checker, cancellationToken);
types.push(...(isRest ? mapDefined(inferred, checker.getElementTypeOfArrayType) : inferred));
}
const type = unifyFromContext(types, checker);
return {

View File

@ -0,0 +1,27 @@
/// <reference path='fourslash.ts' />
// @strictNullChecks: true
////class TokenType {
//// label;
//// token;
//// constructor([|label, token? |]) {
//// this.label = label;
//// this.token = token || "N/A";
//// }
////}
////new TokenType("HI");
verify.codeFix({
description: "Infer parameter types from usage",
index: 2,
newFileContent:
`class TokenType {
label;
token;
constructor(label: string, token?: string | undefined ) {
this.label = label;
this.token = token || "N/A";
}
}
new TokenType("HI");`,
});

View File

@ -0,0 +1,28 @@
/// <reference path='fourslash.ts' />
// @allowJs: true
// @checkJs: true
// @noImplicitAny: true
// @Filename: test.js
////function TokenType([|label, token |]) {
//// this.label = label;
//// this.token = token || "N/A";
////};
////new TokenType("HI")
verify.codeFix({
description: "Infer parameter types from usage",
index: 0,
newFileContent:
`/**
* @param {string} label
* @param {string} [token]
*/
function TokenType(label, token ) {
this.label = label;
this.token = token || "N/A";
};
new TokenType("HI")`,
});

View File

@ -9,4 +9,4 @@
////f(3, false, "s2");
////f(4, "s1", "s2", false, "s4");
verify.rangeAfterCodeFix("...rest: (string | boolean)[]");
verify.rangeAfterCodeFix("...rest: (string | boolean)[]");