Handle JS synthetic rest args in contextual parameter assignment (#48263)

* Handle JS synthetic rest args in contextual parameter assignment

* Limit fixing to only unannotated js rest parameters

* Minimize test

* Add annotated version

* Remove explicit CheckFlags.RestParameter check since apparently not all rest parameters are CheckFlags.RestParameter
This commit is contained in:
Wesley Wigham
2022-03-16 11:04:07 -07:00
committed by GitHub
parent 92bc2ddc3c
commit 7f652509b6
8 changed files with 152 additions and 5 deletions

View File

@@ -12862,7 +12862,19 @@ namespace ts {
p.typeExpression && isJSDocVariadicType(p.typeExpression.type) ? p.typeExpression.type : undefined);
const syntheticArgsSymbol = createSymbol(SymbolFlags.Variable, "args" as __String, CheckFlags.RestParameter);
syntheticArgsSymbol.type = lastParamVariadicType ? createArrayType(getTypeFromTypeNode(lastParamVariadicType.type)) : anyArrayType;
if (lastParamVariadicType) {
// Parameter has effective annotation, lock in type
syntheticArgsSymbol.type = createArrayType(getTypeFromTypeNode(lastParamVariadicType.type));
}
else {
// Parameter has no annotation
// By using a `DeferredType` symbol, we allow the type of this rest arg to be overriden by contextual type assignment so long as its type hasn't been
// cached by `getTypeOfSymbol` yet.
syntheticArgsSymbol.checkFlags |= CheckFlags.DeferredType;
syntheticArgsSymbol.deferralParent = neverType;
syntheticArgsSymbol.deferralConstituents = [anyArrayType];
syntheticArgsSymbol.deferralWriteConstituents = [anyArrayType];
}
if (lastParamVariadicType) {
// Replace the last parameter with a rest parameter.
parameters.pop();
@@ -32163,7 +32175,12 @@ namespace ts {
if (signatureHasRestParameter(signature)) {
// parameter might be a transient symbol generated by use of `arguments` in the function body.
const parameter = last(signature.parameters);
if (isTransientSymbol(parameter) || !getEffectiveTypeAnnotationNode(parameter.valueDeclaration as ParameterDeclaration)) {
if (parameter.valueDeclaration
? !getEffectiveTypeAnnotationNode(parameter.valueDeclaration as ParameterDeclaration)
// a declarationless parameter may still have a `.type` already set by its construction logic
// (which may pull a type from a jsdoc) - only allow fixing on `DeferredType` parameters with a fallback type
: !!(getCheckFlags(parameter) & CheckFlags.DeferredType)
) {
const contextualParameterType = getRestTypeAtPosition(context, len);
assignParameterType(parameter, contextualParameterType);
}
@@ -32182,9 +32199,9 @@ namespace ts {
function assignParameterType(parameter: Symbol, type?: Type) {
const links = getSymbolLinks(parameter);
if (!links.type) {
const declaration = parameter.valueDeclaration as ParameterDeclaration;
links.type = type || getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true);
if (declaration.name.kind !== SyntaxKind.Identifier) {
const declaration = parameter.valueDeclaration as ParameterDeclaration | undefined;
links.type = type || (declaration ? getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true) : getTypeOfSymbol(parameter));
if (declaration && declaration.name.kind !== SyntaxKind.Identifier) {
// if inference didn't come up with anything but unknown, fall back to the binding pattern if present.
if (links.type === unknownType) {
links.type = getTypeFromBindingPattern(declaration.name);