GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — Quick fix for no-implicit-any errors to add explicit type annotation by mhegazy · Pull Request #14786 · microsoft/TypeScript Conversation
Contributor
Add a new quick fix for no-implicit-any errors for variables, parameters, and members. The fix tries to "infer" the type from assignments to the symbol in question, and serializes the type in an explicit type annotation addressing the no-implicit-any error.
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. All reactions
Contributor
All reactions
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page.
Daniel Rosenwasser (DanielRosenwasser)
requested changes
|
| 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); |
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I feel like there should be assertions when you cast based on the error code you're matching on.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
We asserted a few lines earlier that it has to be an identifier, DotDotDotToken, PublicKeyword, ProtectedKeyword, or ReadonlyKeyword. since this is a variable declaration error, we know it can not be anything that is not an identifier (i.e. binding pattern).
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| case Diagnostics.Variable_0_implicitly_has_an_1_type.code: | ||
| return getCodeActionForVariableUsage(<Identifier>token); | ||
|
|
||
| // Paramtert declarations |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Parameter
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Done
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| return typeString; | ||
| } | ||
|
|
||
| function getParameterIndexInList(parameter: ParameterDeclaration, list: NodeArray<ParameterDeclaration>) { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Isn't this just indexOf?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| for (const child of node.getChildren(sourcefile)) { | ||
| if (child.kind === kind) return child; | ||
| } | ||
| } |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
explicit return undefined
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
done.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| return -1; | ||
| } | ||
|
|
||
| function getFirstChildOfKind(node: Node, sourcefile: SourceFile, kind: SyntaxKind) { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
There's a findChildOfKind which is nice and slow and allocates the lamdba you were trying to avoid here.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
||
| // Property declarations | ||
| case Diagnostics.Member_0_implicitly_has_an_1_type.code: | ||
| return getCodeActionForVariableDeclaration(<VariableDeclaration>token.parent); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Is there ever a time that you won't have a symbol that you can't call this directly? What about in the case of a computed property?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
We filter these earlier on. it can only be identifier for a var declarations
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Conflicts: src/compiler/checker.ts src/compiler/diagnosticMessages.json src/compiler/types.ts src/services/tsconfig.json
| getUndefinedType: () => undefinedType, | ||
| getNullType: () => nullType, | ||
| getESSymbolType: () => esSymbolType, | ||
| getNeverType: () => neverType, |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
As a concern, we have multiple never types internally. I feel somewhat uneasy about exposing it.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
made them all them internal for now.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Add new lines to the end of files? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Daniel Rosenwasser (@DanielRosenwasser) any more comments? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
A few questions about inference, plus I noticed some typos.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| case Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation.code: | ||
| return isSetAccessor(containingFunction) ? getCodeActionForSetAccessor(containingFunction) : undefined; | ||
|
|
||
| // Property declarations |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This looks redundant with the very first case of this switch.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
thanks. removed.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| errorCodes: [ | ||
| // Variable declarations | ||
| Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined.code, | ||
| Diagnostics.Variable_0_implicitly_has_an_1_type.code, |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
this error maps to getCodeActionForVariableUsage, so I think it is actually should be commented with // variable uses
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
sure :)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| return undefined; | ||
| } | ||
|
|
||
| const types = inferTypeForParametersFromUsage(containingFunction) || |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
so it's either from usage from calls or from intra-function usages, but not both? Why have inferences from call sites win?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
If we do the function body first we are guranteed to manifacture something that the makes the funciton happy, but it usually will be too general. giving usage precedence allows us to get more specific types.. e.g.:
function length(a) { return a.length; } length([1,2,3]);
inferring from function body will give a a type { length: any}. which is accurate, but rarely what the user would write manually.
inferring from the call site gives us number[] which is more like it..
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Perhaps you should infer <T extends { length }>(a: T) => ... instead. In combination with return type inference, this should yield <T extends { length }>(a: T) => T["length"], which will work correctly when applied to arrays (or indeed anything with a length property).
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I tried that.. the types get unwieldy very fast.. and you have this structural type that has length, push, splice, with an element that is has kind, instead of just Node[]. the call sites provided the more user-identifiable name.. though the first one is more correct.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| const types = inferTypeForParametersFromUsage(containingFunction) || | ||
| map(containingFunction.parameters, p => isIdentifier(p.name) && inferTypeForVariableFromUsage(p.name)); | ||
|
|
||
| if (types) { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
it would be easier to read both nested blocks as early returns (if (!types) return undefined and if (!textChanges) return undefined.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
done.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
||
| if (types) { | ||
| const textChanges = reduceLeft(containingFunction.parameters, (memo, parameter, index) => { | ||
| if (types[index] && !parameter.type && !parameter.initializer) { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
containingFunction.parameters.length === types.length, right?
If so, I don't think we need the efficiency of reduce and lazy initialisation of memo. It would be easier to read as a zip followed by filtering out undefined:
zipWith(containingFunction.parameters, types, (param, type) => ...).filter(change => change !== undefined)
(zipWith is in core.ts so it might need to be moved into the public API?)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| } | ||
|
|
||
| if (usageContext.callContexts) { | ||
| for (const callConext of usageContext.callContexts) { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
typo:callContext
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
done
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| return checker.createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, callContext.argumentTypes.length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); | ||
| } | ||
|
|
||
| function inferTypeFromContext(node: Expression, checker: TypeChecker, usageContext: UsageContext): void { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Can you move this before getTypeFromUsageContext since it's used before?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Ok, moved get* after infer*
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| return; | ||
| } | ||
| else { | ||
| const indextType = checker.getTypeAtLocation(parent); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
probably is a typo for indexType ?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
done
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| 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(`p${i}`)); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I would call this arg${i} to match our other names for unnamed parameters.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
done
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| 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) { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
what happens if you combine object types made up of the synthetic properties (et al) with the candidate types? Are the candidate types strictly better than the synthetic object type?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I would think so.. a user-defined type seems more like what you would put on a function, e.g. Node or Symbol, and not {kind: SyntaxKind} or {flags: SymbolFlags} to use an example of our code base.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.