Merge pull request #26481 from Microsoft/simplifyResolveCall

Simplify call resolution logic
This commit is contained in:
Anders Hejlsberg 2018-08-17 10:51:12 -07:00 committed by GitHub
commit b746da2a68
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 217 additions and 465 deletions

View file

@ -15841,10 +15841,10 @@ namespace ts {
}
const iife = getImmediatelyInvokedFunctionExpression(func);
if (iife && iife.arguments) {
const args = getEffectiveCallArguments(iife)!;
const args = getEffectiveCallArguments(iife);
const indexOfParameter = func.parameters.indexOf(parameter);
if (parameter.dotDotDotToken) {
return getSpreadArgumentType(iife, args, indexOfParameter, args.length, anyType, /*context*/ undefined);
return getSpreadArgumentType(args, indexOfParameter, args.length, anyType, /*context*/ undefined);
}
const links = getNodeLinks(iife);
const cached = links.resolvedSignature;
@ -15970,7 +15970,7 @@ namespace ts {
// In a typed function call, an argument or substitution expression is contextually typed by the type of the corresponding parameter.
function getContextualTypeForArgument(callTarget: CallLikeExpression, arg: Expression): Type | undefined {
const args = getEffectiveCallArguments(callTarget)!; // TODO: GH#18217
const args = getEffectiveCallArguments(callTarget);
const argIndex = args.indexOf(arg); // -1 for e.g. the expression of a CallExpression, or the tag of a TaggedTemplateExpression
return argIndex === -1 ? undefined : getContextualTypeForArgumentAtIndex(callTarget, argIndex);
}
@ -18456,20 +18456,16 @@ namespace ts {
}
function hasCorrectArity(node: CallLikeExpression, args: ReadonlyArray<Expression>, signature: Signature, signatureHelpTrailingComma = false) {
let argCount: number; // Apparent number of arguments we will have in this call
let callIsIncomplete = false; // In incomplete call we want to be lenient when we have too few arguments
let spreadArgIndex = -1;
if (isJsxOpeningLikeElement(node)) {
// The arity check will be done in "checkApplicableSignatureForJsxOpeningLikeElement".
return true;
}
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
// Even if the call is incomplete, we'll have a missing expression as our last argument,
// so we can say the count is just the arg list length
argCount = args.length;
let argCount: number;
let callIsIncomplete = false; // In incomplete call we want to be lenient when we have too few arguments
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
argCount = args.length;
if (node.template.kind === SyntaxKind.TemplateExpression) {
// If a tagged template expression lacks a tail literal, the call is incomplete.
// Specifically, a template only can end in a TemplateTail or a Missing literal.
@ -18486,7 +18482,7 @@ namespace ts {
}
}
else if (node.kind === SyntaxKind.Decorator) {
argCount = getEffectiveArgumentCount(node, /*args*/ undefined!, signature);
argCount = getDecoratorArgumentCount(node, signature);
}
else {
if (!node.arguments) {
@ -18500,12 +18496,11 @@ namespace ts {
// If we are missing the close parenthesis, the call is incomplete.
callIsIncomplete = node.arguments.end === node.end;
spreadArgIndex = getSpreadArgumentIndex(args);
}
// If a spread argument is present, check that it corresponds to a rest parameter or at least that it's in the valid range.
if (spreadArgIndex >= 0) {
return spreadArgIndex >= getMinArgumentCount(signature) && (hasEffectiveRestParameter(signature) || spreadArgIndex < getParameterCount(signature));
// If a spread argument is present, check that it corresponds to a rest parameter or at least that it's in the valid range.
const spreadArgIndex = getSpreadArgumentIndex(args);
if (spreadArgIndex >= 0) {
return spreadArgIndex >= getMinArgumentCount(signature) && (hasEffectiveRestParameter(signature) || spreadArgIndex < getParameterCount(signature));
}
}
// Too many arguments implies incorrect arity.
@ -18614,55 +18609,31 @@ namespace ts {
inferTypes(context.inferences, thisArgumentType, thisType);
}
// We perform two passes over the arguments. In the first pass we infer from all arguments, but use
// wildcards for all context sensitive function expressions.
const effectiveArgCount = getEffectiveArgumentCount(node, args, signature);
const genericRestType = getGenericRestType(signature);
const argCount = genericRestType ? Math.min(getParameterCount(signature) - 1, effectiveArgCount) : effectiveArgCount;
const argCount = genericRestType ? Math.min(getParameterCount(signature) - 1, args.length) : args.length;
for (let i = 0; i < argCount; i++) {
const arg = getEffectiveArgument(node, args, i);
// If the effective argument is 'undefined', then it is an argument that is present but is synthetic.
if (arg === undefined || arg.kind !== SyntaxKind.OmittedExpression) {
const arg = args[i];
if (arg.kind !== SyntaxKind.OmittedExpression) {
const paramType = getTypeAtPosition(signature, i);
let argType = getEffectiveArgumentType(node, i);
// If the effective argument type is 'undefined', there is no synthetic type
// for the argument. In that case, we should check the argument.
if (argType === undefined) {
// For context sensitive arguments we pass the identityMapper, which is a signal to treat all
// context sensitive function expressions as wildcards
const mapper = excludeArgument && excludeArgument[i] !== undefined ? identityMapper : context;
argType = checkExpressionWithContextualType(arg!, paramType, mapper);
}
// For context sensitive arguments we pass the identityMapper, which is a signal to treat all
// context sensitive function expressions as wildcards
const mapper = excludeArgument && excludeArgument[i] !== undefined ? identityMapper : context;
const argType = checkExpressionWithContextualType(arg, paramType, mapper);
inferTypes(context.inferences, argType, paramType);
}
}
if (genericRestType) {
const spreadType = getSpreadArgumentType(node, args, argCount, effectiveArgCount, genericRestType, context);
const spreadType = getSpreadArgumentType(args, argCount, args.length, genericRestType, context);
inferTypes(context.inferences, spreadType, genericRestType);
}
// In the second pass we visit only context sensitive arguments, and only those that aren't excluded, this
// time treating function expressions normally (which may cause previously inferred type arguments to be fixed
// as we construct types for contextually typed parameters)
// Decorators will not have `excludeArgument`, as their arguments cannot be contextually typed.
// Tagged template expressions will always have `undefined` for `excludeArgument[0]`.
if (excludeArgument) {
for (let i = 0; i < argCount; i++) {
// No need to check for omitted args and template expressions, their exclusion value is always undefined
if (excludeArgument[i] === false) {
const arg = args[i];
const paramType = getTypeAtPosition(signature, i);
inferTypes(context.inferences, checkExpressionWithContextualType(arg, paramType, context), paramType);
}
}
}
return getInferredTypes(context);
}
function getSpreadArgumentType(node: CallLikeExpression, args: ReadonlyArray<Expression>, index: number, argCount: number, restType: TypeParameter, context: InferenceContext | undefined) {
function getSpreadArgumentType(args: ReadonlyArray<Expression>, index: number, argCount: number, restType: TypeParameter, context: InferenceContext | undefined) {
if (index >= argCount - 1) {
const arg = getEffectiveArgument(node, args, argCount - 1);
const arg = args[argCount - 1];
if (isSpreadArgument(arg)) {
// We are inferring from a spread expression in the last argument position, i.e. both the parameter
// and the argument are ...x forms.
@ -18676,12 +18647,9 @@ namespace ts {
const types = [];
let spreadIndex = -1;
for (let i = index; i < argCount; i++) {
let argType = getEffectiveArgumentType(node, i);
if (!argType) {
argType = checkExpressionWithContextualType(args[i], contextualType, context);
if (spreadIndex < 0 && isSpreadArgument(args[i])) {
spreadIndex = i - index;
}
const argType = checkExpressionWithContextualType(args[i], contextualType, context);
if (spreadIndex < 0 && isSpreadArgument(args[i])) {
spreadIndex = i - index;
}
types.push(hasPrimitiveContextualType ? getRegularTypeOfLiteralType(argType) : getWidenedLiteralType(argType));
}
@ -18690,7 +18658,7 @@ namespace ts {
createTupleType(append(types.slice(0, spreadIndex), getUnionType(types.slice(spreadIndex))), spreadIndex, /*hasRestElement*/ true);
}
function checkTypeArguments(signature: Signature, typeArgumentNodes: ReadonlyArray<TypeNode>, reportErrors: boolean, headMessage?: DiagnosticMessage): Type[] | false {
function checkTypeArguments(signature: Signature, typeArgumentNodes: ReadonlyArray<TypeNode>, reportErrors: boolean, headMessage?: DiagnosticMessage): Type[] | undefined {
const isJavascript = isInJavaScriptFile(signature.declaration);
const typeParameters = signature.typeParameters!;
const typeArgumentTypes = fillMissingTypeArguments(map(typeArgumentNodes, getTypeFromTypeNode), typeParameters, getMinTypeArgumentCount(typeParameters), isJavascript);
@ -18698,21 +18666,21 @@ namespace ts {
for (let i = 0; i < typeArgumentNodes.length; i++) {
Debug.assert(typeParameters[i] !== undefined, "Should not call checkTypeArguments with too many type arguments");
const constraint = getConstraintOfTypeParameter(typeParameters[i]);
if (!constraint) continue;
const errorInfo = reportErrors && headMessage ? (() => chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Type_0_does_not_satisfy_the_constraint_1)) : undefined;
const typeArgumentHeadMessage = headMessage || Diagnostics.Type_0_does_not_satisfy_the_constraint_1;
if (!mapper) {
mapper = createTypeMapper(typeParameters, typeArgumentTypes);
}
const typeArgument = typeArgumentTypes[i];
if (!checkTypeAssignableTo(
typeArgument,
getTypeWithThisArgument(instantiateType(constraint, mapper), typeArgument),
reportErrors ? typeArgumentNodes[i] : undefined,
typeArgumentHeadMessage,
errorInfo)) {
return false;
if (constraint) {
const errorInfo = reportErrors && headMessage ? (() => chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Type_0_does_not_satisfy_the_constraint_1)) : undefined;
const typeArgumentHeadMessage = headMessage || Diagnostics.Type_0_does_not_satisfy_the_constraint_1;
if (!mapper) {
mapper = createTypeMapper(typeParameters, typeArgumentTypes);
}
const typeArgument = typeArgumentTypes[i];
if (!checkTypeAssignableTo(
typeArgument,
getTypeWithThisArgument(instantiateType(constraint, mapper), typeArgument),
reportErrors ? typeArgumentNodes[i] : undefined,
typeArgumentHeadMessage,
errorInfo)) {
return undefined;
}
}
}
return typeArgumentTypes;
@ -18776,31 +18744,23 @@ namespace ts {
}
}
const headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1;
const argCount = getEffectiveArgumentCount(node, args, signature);
const restIndex = signature.hasRestParameter ? signature.parameters.length - 1 : -1;
const restType = restIndex >= 0 ? getTypeOfSymbol(signature.parameters[restIndex]) : anyType;
for (let i = 0; i < argCount; i++) {
const arg = getEffectiveArgument(node, args, i);
// If the effective argument is 'undefined', then it is an argument that is present but is synthetic.
if (arg === undefined || arg.kind !== SyntaxKind.OmittedExpression) {
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg.kind !== SyntaxKind.OmittedExpression) {
if (i === restIndex && (restType.flags & TypeFlags.TypeParameter || isSpreadArgument(arg) && !isArrayType(restType))) {
const spreadType = getSpreadArgumentType(node, args, i, argCount, restType, /*context*/ undefined);
const spreadType = getSpreadArgumentType(args, i, args.length, restType, /*context*/ undefined);
return checkTypeRelatedTo(spreadType, restType, relation, arg, headMessage);
}
else {
// Check spread elements against rest type (from arity check we know spread argument corresponds to a rest parameter)
const paramType = getTypeAtPosition(signature, i);
// If the effective argument type is undefined, there is no synthetic type for the argument.
// In that case, we should check the argument.
const argType = getEffectiveArgumentType(node, i) ||
checkExpressionWithContextualType(arg!, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
const argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
// If one or more arguments are still excluded (as indicated by a non-null excludeArgument parameter),
// we obtain the regular type of any object literal arguments because we may not have inferred complete
// parameter types yet and therefore excess property checks may yield false positives (see #17041).
const checkArgType = excludeArgument ? getRegularTypeOfObjectLiteral(argType) : argType;
// Use argument expression as error location when reporting errors
const errorNode = reportErrors ? getEffectiveArgumentErrorNode(node, i, arg) : undefined;
if (!checkTypeRelatedTo(checkArgType, paramType, relation, errorNode, headMessage)) {
if (!checkTypeRelatedTo(checkArgType, paramType, relation, reportErrors ? arg : undefined, headMessage)) {
return false;
}
}
@ -18821,19 +18781,21 @@ namespace ts {
}
}
function createSyntheticExpression(parent: Node, type: Type, isSpread?: boolean) {
const result = <SyntheticExpression>createNode(SyntaxKind.SyntheticExpression, parent.pos, parent.end);
result.parent = parent;
result.type = type;
result.isSpread = isSpread || false;
return result;
}
/**
* Returns the effective arguments for an expression that works like a function invocation.
*
* If 'node' is a CallExpression or a NewExpression, then its argument list is returned.
* If 'node' is a TaggedTemplateExpression, a new argument list is constructed from the substitution
* expressions, where the first element of the list is `undefined`.
* If 'node' is a Decorator, the argument list will be `undefined`, and its arguments and types
* will be supplied from calls to `getEffectiveArgumentCount` and `getEffectiveArgumentType`.
*/
function getEffectiveCallArguments(node: CallLikeExpression): ReadonlyArray<Expression> | undefined {
function getEffectiveCallArguments(node: CallLikeExpression): ReadonlyArray<Expression> {
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
const template = node.template;
const args: Expression[] = [undefined!]; // TODO: GH#18217
const args: Expression[] = [createSyntheticExpression(template, getGlobalTemplateStringsArrayType())];
if (template.kind === SyntaxKind.TemplateExpression) {
forEach(template.templateSpans, span => {
args.push(span.expression);
@ -18841,314 +18803,89 @@ namespace ts {
}
return args;
}
else if (node.kind === SyntaxKind.Decorator) {
// For a decorator, we return undefined as we will determine
// the number and types of arguments for a decorator using
// `getEffectiveArgumentCount` and `getEffectiveArgumentType` below.
return undefined;
if (node.kind === SyntaxKind.Decorator) {
return getEffectiveDecoratorArguments(node);
}
else if (isJsxOpeningLikeElement(node)) {
if (isJsxOpeningLikeElement(node)) {
return node.attributes.properties.length > 0 ? [node.attributes] : emptyArray;
}
else {
const args = node.arguments || emptyArray;
const length = args.length;
if (length && isSpreadArgument(args[length - 1]) && getSpreadArgumentIndex(args) === length - 1) {
// We have a spread argument in the last position and no other spread arguments. If the type
// of the argument is a tuple type, spread the tuple elements into the argument list. We can
// call checkExpressionCached because spread expressions never have a contextual type.
const spreadArgument = <SpreadElement>args[length - 1];
const type = checkExpressionCached(spreadArgument.expression);
if (isTupleType(type)) {
const typeArguments = (<TypeReference>type).typeArguments || emptyArray;
const restIndex = type.target.hasRestElement ? typeArguments.length - 1 : -1;
const syntheticArgs = map(typeArguments, (t, i) => {
const arg = <SyntheticExpression>createNode(SyntaxKind.SyntheticExpression, spreadArgument.pos, spreadArgument.end);
arg.parent = spreadArgument;
arg.type = t;
arg.isSpread = i === restIndex;
return arg;
});
return concatenate(args.slice(0, length - 1), syntheticArgs);
}
}
return args;
}
}
/**
* Returns the effective argument count for a node that works like a function invocation.
* If 'node' is a Decorator, the number of arguments is derived from the decoration
* target and the signature:
* If 'node.target' is a class declaration or class expression, the effective argument
* count is 1.
* If 'node.target' is a parameter declaration, the effective argument count is 3.
* If 'node.target' is a property declaration, the effective argument count is 2.
* If 'node.target' is a method or accessor declaration, the effective argument count
* is 3, although it can be 2 if the signature only accepts two arguments, allowing
* us to match a property decorator.
* Otherwise, the argument count is the length of the 'args' array.
*/
function getEffectiveArgumentCount(node: CallLikeExpression, args: ReadonlyArray<Expression>, signature: Signature) {
if (node.kind === SyntaxKind.Decorator) {
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
// A class decorator will have one argument (see `ClassDecorator` in core.d.ts)
return 1;
case SyntaxKind.PropertyDeclaration:
// A property declaration decorator will have two arguments (see
// `PropertyDecorator` in core.d.ts)
return 2;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
// A method or accessor declaration decorator will have two or three arguments (see
// `PropertyDecorator` and `MethodDecorator` in core.d.ts)
// If we are emitting decorators for ES3, we will only pass two arguments.
if (languageVersion === ScriptTarget.ES3) {
return 2;
}
// If the method decorator signature only accepts a target and a key, we will only
// type check those arguments.
return signature.parameters.length >= 3 ? 3 : 2;
case SyntaxKind.Parameter:
// A parameter declaration decorator will have three arguments (see
// `ParameterDecorator` in core.d.ts)
return 3;
default:
return Debug.fail();
const args = node.arguments || emptyArray;
const length = args.length;
if (length && isSpreadArgument(args[length - 1]) && getSpreadArgumentIndex(args) === length - 1) {
// We have a spread argument in the last position and no other spread arguments. If the type
// of the argument is a tuple type, spread the tuple elements into the argument list. We can
// call checkExpressionCached because spread expressions never have a contextual type.
const spreadArgument = <SpreadElement>args[length - 1];
const type = checkExpressionCached(spreadArgument.expression);
if (isTupleType(type)) {
const typeArguments = (<TypeReference>type).typeArguments || emptyArray;
const restIndex = type.target.hasRestElement ? typeArguments.length - 1 : -1;
const syntheticArgs = map(typeArguments, (t, i) => createSyntheticExpression(spreadArgument, t, /*isSpread*/ i === restIndex));
return concatenate(args.slice(0, length - 1), syntheticArgs);
}
}
else {
return args.length;
}
return args;
}
/**
* Returns the effective type of the first argument to a decorator.
* If 'node' is a class declaration or class expression, the effective argument type
* is the type of the static side of the class.
* If 'node' is a parameter declaration, the effective argument type is either the type
* of the static or instance side of the class for the parameter's parent method,
* depending on whether the method is declared static.
* For a constructor, the type is always the type of the static side of the class.
* If 'node' is a property, method, or accessor declaration, the effective argument
* type is the type of the static or instance side of the parent class for class
* element, depending on whether the element is declared static.
* Returns the synthetic argument list for a decorator invocation.
*/
function getEffectiveDecoratorFirstArgumentType(node: Node): Type {
// The first argument to a decorator is its `target`.
if (node.kind === SyntaxKind.ClassDeclaration) {
// For a class decorator, the `target` is the type of the class (e.g. the
// "static" or "constructor" side of the class)
const classSymbol = getSymbolOfNode(node as ClassDeclaration);
return getTypeOfSymbol(classSymbol);
function getEffectiveDecoratorArguments(node: Decorator): ReadonlyArray<Expression> {
const parent = node.parent;
const expr = node.expression;
switch (parent.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
// For a class decorator, the `target` is the type of the class (e.g. the
// "static" or "constructor" side of the class).
return [
createSyntheticExpression(expr, getTypeOfSymbol(getSymbolOfNode(parent)))
];
case SyntaxKind.Parameter:
// A parameter declaration decorator will have three arguments (see
// `ParameterDecorator` in core.d.ts).
const func = <FunctionLikeDeclaration>parent.parent;
return [
createSyntheticExpression(expr, parent.parent.kind === SyntaxKind.Constructor ? getTypeOfSymbol(getSymbolOfNode(func)) : errorType),
createSyntheticExpression(expr, anyType),
createSyntheticExpression(expr, numberType)
];
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
// A method or accessor declaration decorator will have two or three arguments (see
// `PropertyDecorator` and `MethodDecorator` in core.d.ts). If we are emitting decorators
// for ES3, we will only pass two arguments.
const hasPropDesc = parent.kind !== SyntaxKind.PropertyDeclaration && languageVersion !== ScriptTarget.ES3;
return [
createSyntheticExpression(expr, getParentTypeOfClassElement(<ClassElement>parent)),
createSyntheticExpression(expr, getClassElementPropertyKeyType(<ClassElement>parent)),
createSyntheticExpression(expr, hasPropDesc ? createTypedPropertyDescriptorType(getTypeOfNode(parent)) : anyType)
];
}
if (node.kind === SyntaxKind.Parameter) {
// For a parameter decorator, the `target` is the parent type of the
// parameter's containing method.
node = node.parent;
if (node.kind === SyntaxKind.Constructor) {
const classSymbol = getSymbolOfNode(node as ConstructorDeclaration);
return getTypeOfSymbol(classSymbol);
}
}
if (node.kind === SyntaxKind.PropertyDeclaration ||
node.kind === SyntaxKind.MethodDeclaration ||
node.kind === SyntaxKind.GetAccessor ||
node.kind === SyntaxKind.SetAccessor) {
// For a property or method decorator, the `target` is the
// "static"-side type of the parent of the member if the member is
// declared "static"; otherwise, it is the "instance"-side type of the
// parent of the member.
return getParentTypeOfClassElement(<ClassElement>node);
}
Debug.fail("Unsupported decorator target.");
return errorType;
return Debug.fail();
}
/**
* Returns the effective type for the second argument to a decorator.
* If 'node' is a parameter, its effective argument type is one of the following:
* If 'node.parent' is a constructor, the effective argument type is 'any', as we
* will emit `undefined`.
* If 'node.parent' is a member with an identifier, numeric, or string literal name,
* the effective argument type will be a string literal type for the member name.
* If 'node.parent' is a computed property name, the effective argument type will
* either be a symbol type or the string type.
* If 'node' is a member with an identifier, numeric, or string literal name, the
* effective argument type will be a string literal type for the member name.
* If 'node' is a computed property name, the effective argument type will either
* be a symbol type or the string type.
* A class decorator does not have a second argument type.
* Returns the argument count for a decorator node that works like a function invocation.
*/
function getEffectiveDecoratorSecondArgumentType(node: Node) {
// The second argument to a decorator is its `propertyKey`
if (node.kind === SyntaxKind.ClassDeclaration) {
Debug.fail("Class decorators should not have a second synthetic argument.");
return errorType;
}
if (node.kind === SyntaxKind.Parameter) {
node = node.parent;
if (node.kind === SyntaxKind.Constructor) {
// For a constructor parameter decorator, the `propertyKey` will be `undefined`.
return anyType;
}
// For a non-constructor parameter decorator, the `propertyKey` will be either
// a string or a symbol, based on the name of the parameter's containing method.
}
if (node.kind === SyntaxKind.PropertyDeclaration ||
node.kind === SyntaxKind.MethodDeclaration ||
node.kind === SyntaxKind.GetAccessor ||
node.kind === SyntaxKind.SetAccessor) {
// The `propertyKey` for a property or method decorator will be a
// string literal type if the member name is an identifier, number, or string;
// otherwise, if the member name is a computed property name it will
// be either string or symbol.
const element = <ClassElement>node;
const name = element.name!;
switch (name.kind) {
case SyntaxKind.Identifier:
return getLiteralType(idText(name));
case SyntaxKind.NumericLiteral:
case SyntaxKind.StringLiteral:
return getLiteralType(name.text);
case SyntaxKind.ComputedPropertyName:
const nameType = checkComputedPropertyName(name);
if (isTypeAssignableToKind(nameType, TypeFlags.ESSymbolLike)) {
return nameType;
}
else {
return stringType;
}
default:
Debug.fail("Unsupported property name.");
return errorType;
}
}
Debug.fail("Unsupported decorator target.");
return errorType;
}
/**
* Returns the effective argument type for the third argument to a decorator.
* If 'node' is a parameter, the effective argument type is the number type.
* If 'node' is a method or accessor, the effective argument type is a
* `TypedPropertyDescriptor<T>` instantiated with the type of the member.
* Class and property decorators do not have a third effective argument.
*/
function getEffectiveDecoratorThirdArgumentType(node: Node) {
// The third argument to a decorator is either its `descriptor` for a method decorator
// or its `parameterIndex` for a parameter decorator
if (node.kind === SyntaxKind.ClassDeclaration) {
Debug.fail("Class decorators should not have a third synthetic argument.");
return errorType;
}
if (node.kind === SyntaxKind.Parameter) {
// The `parameterIndex` for a parameter decorator is always a number
return numberType;
}
if (node.kind === SyntaxKind.PropertyDeclaration) {
Debug.fail("Property decorators should not have a third synthetic argument.");
return errorType;
}
if (node.kind === SyntaxKind.MethodDeclaration ||
node.kind === SyntaxKind.GetAccessor ||
node.kind === SyntaxKind.SetAccessor) {
// The `descriptor` for a method decorator will be a `TypedPropertyDescriptor<T>`
// for the type of the member.
const propertyType = getTypeOfNode(node);
return createTypedPropertyDescriptorType(propertyType);
}
Debug.fail("Unsupported decorator target.");
return errorType;
}
/**
* Returns the effective argument type for the provided argument to a decorator.
*/
function getEffectiveDecoratorArgumentType(node: Decorator, argIndex: number): Type {
if (argIndex === 0) {
return getEffectiveDecoratorFirstArgumentType(node.parent);
}
else if (argIndex === 1) {
return getEffectiveDecoratorSecondArgumentType(node.parent);
}
else if (argIndex === 2) {
return getEffectiveDecoratorThirdArgumentType(node.parent);
}
Debug.fail("Decorators should not have a fourth synthetic argument.");
return errorType;
}
/**
* Gets the effective argument type for an argument in a call expression.
*/
function getEffectiveArgumentType(node: CallLikeExpression, argIndex: number): Type | undefined {
// Decorators provide special arguments, a tagged template expression provides
// a special first argument, and string literals get string literal types
// unless we're reporting errors
if (node.kind === SyntaxKind.Decorator) {
return getEffectiveDecoratorArgumentType(node, argIndex);
}
else if (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression) {
return getGlobalTemplateStringsArrayType();
}
// This is not a synthetic argument, so we return 'undefined'
// to signal that the caller needs to check the argument.
return undefined;
}
/**
* Gets the effective argument expression for an argument in a call expression.
*/
function getEffectiveArgument(node: CallLikeExpression, args: ReadonlyArray<Expression>, argIndex: number) {
// For a decorator or the first argument of a tagged template expression we return undefined.
if (node.kind === SyntaxKind.Decorator ||
(argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression)) {
return undefined;
}
return args[argIndex];
}
/**
* Gets the error node to use when reporting errors for an effective argument.
*/
function getEffectiveArgumentErrorNode(node: CallLikeExpression, argIndex: number, arg: Expression | undefined): Expression | undefined {
if (node.kind === SyntaxKind.Decorator) {
// For a decorator, we use the expression of the decorator for error reporting.
return node.expression;
}
else if (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression) {
// For a the first argument of a tagged template expression, we use the template of the tag for error reporting.
return node.template;
}
else {
return arg;
function getDecoratorArgumentCount(node: Decorator, signature: Signature) {
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
return 1;
case SyntaxKind.PropertyDeclaration:
return 2;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
// For ES3 or decorators with only two parameters we supply only two arguments
return languageVersion === ScriptTarget.ES3 || signature.parameters.length <= 2 ? 2 : 3;
case SyntaxKind.Parameter:
return 3;
default:
return Debug.fail();
}
}
@ -19226,33 +18963,29 @@ namespace ts {
const args = getEffectiveCallArguments(node);
// The following applies to any value of 'excludeArgument[i]':
// - true: the argument at 'i' is susceptible to a one-time permanent contextual typing.
// - undefined: the argument at 'i' is *not* susceptible to permanent contextual typing.
// - false: the argument at 'i' *was* and *has been* permanently contextually typed.
// The excludeArgument array contains true for each context sensitive argument (an argument
// is context sensitive it is susceptible to a one-time permanent contextual typing).
//
// The idea is that we will perform type argument inference & assignability checking once
// without using the susceptible parameters that are functions, and once more for each of those
// without using the susceptible parameters that are functions, and once more for those
// parameters, contextually typing each as we go along.
//
// For a tagged template, then the first argument be 'undefined' if necessary
// because it represents a TemplateStringsArray.
// For a tagged template, then the first argument be 'undefined' if necessary because it
// represents a TemplateStringsArray.
//
// For a decorator, no arguments are susceptible to contextual typing due to the fact
// decorators are applied to a declaration by the emitter, and not to an expression.
const isSingleNonGenericCandidate = candidates.length === 1 && !candidates[0].typeParameters;
let excludeArgument: boolean[] | undefined;
let excludeCount = 0;
if (!isDecorator && !isSingleNonGenericCandidate) {
// We do not need to call `getEffectiveArgumentCount` here as it only
// applies when calculating the number of arguments for a decorator.
for (let i = isTaggedTemplate ? 1 : 0; i < args!.length; i++) {
if (isContextSensitive(args![i])) {
for (let i = 0; i < args.length; i++) {
if (isContextSensitive(args[i])) {
if (!excludeArgument) {
excludeArgument = new Array(args!.length);
excludeArgument = new Array(args.length);
}
excludeArgument[i] = true;
excludeCount++;
}
}
}
@ -19322,10 +19055,10 @@ namespace ts {
// in arguments too early. If possible, we'd like to only type them once we know the correct
// overload. However, this matters for the case where the call is correct. When the call is
// an error, we don't need to exclude any arguments, although it would cause no harm to do so.
checkApplicableSignature(node, args!, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
}
else if (candidateForArgumentArityError) {
diagnostics.add(getArgumentArityError(node, [candidateForArgumentArityError], args!));
diagnostics.add(getArgumentArityError(node, [candidateForArgumentArityError], args));
}
else if (candidateForTypeArgumentError) {
checkTypeArguments(candidateForTypeArgumentError, (node as CallExpression | TaggedTemplateExpression).typeArguments!, /*reportErrors*/ true, fallbackError);
@ -19333,7 +19066,7 @@ namespace ts {
else if (typeArguments && every(signatures, sig => typeArguments!.length < getMinTypeArgumentCount(sig.typeParameters) || typeArguments!.length > length(sig.typeParameters))) {
diagnostics.add(getTypeArgumentArityError(node, signatures, typeArguments));
}
else if (args) {
else if (!isDecorator) {
diagnostics.add(getArgumentArityError(node, signatures, args));
}
else if (fallbackError) {
@ -19349,10 +19082,10 @@ namespace ts {
if (isSingleNonGenericCandidate) {
const candidate = candidates[0];
if (typeArguments || !hasCorrectArity(node, args!, candidate, signatureHelpTrailingComma)) {
if (typeArguments || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) {
return undefined;
}
if (!checkApplicableSignature(node, args!, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
if (!checkApplicableSignature(node, args, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
candidateForArgumentError = candidate;
return undefined;
}
@ -19360,58 +19093,58 @@ namespace ts {
}
for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
const originalCandidate = candidates[candidateIndex];
if (!hasCorrectTypeArgumentArity(originalCandidate, typeArguments) || !hasCorrectArity(node, args!, originalCandidate, signatureHelpTrailingComma)) {
const candidate = candidates[candidateIndex];
if (!hasCorrectTypeArgumentArity(candidate, typeArguments) || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) {
continue;
}
let candidate: Signature;
const inferenceContext = originalCandidate.typeParameters ?
createInferenceContext(originalCandidate.typeParameters, originalCandidate, /*flags*/ isInJavaScriptFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None) :
undefined;
let checkCandidate: Signature;
let inferenceContext: InferenceContext | undefined;
while (true) {
candidate = originalCandidate;
if (candidate.typeParameters) {
let typeArgumentTypes: Type[];
if (typeArguments) {
const typeArgumentResult = checkTypeArguments(candidate, typeArguments, /*reportErrors*/ false);
if (typeArgumentResult) {
typeArgumentTypes = typeArgumentResult;
}
else {
candidateForTypeArgumentError = originalCandidate;
break;
}
if (candidate.typeParameters) {
let typeArgumentTypes: Type[] | undefined;
if (typeArguments) {
typeArgumentTypes = checkTypeArguments(candidate, typeArguments, /*reportErrors*/ false);
if (!typeArgumentTypes) {
candidateForTypeArgumentError = candidate;
continue;
}
else {
typeArgumentTypes = inferTypeArguments(node, candidate, args!, excludeArgument, inferenceContext!);
}
const isJavascript = isInJavaScriptFile(candidate.declaration);
candidate = getSignatureInstantiation(candidate, typeArgumentTypes, isJavascript);
// If the original signature has a generic rest type, instantiation may produce a
// signature with different arity and we need to perform another arity check.
if (getGenericRestType(originalCandidate) && !hasCorrectArity(node, args!, candidate, signatureHelpTrailingComma)) {
candidateForArgumentArityError = candidate;
break;
}
}
if (!checkApplicableSignature(node, args!, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
candidateForArgumentError = candidate;
break;
}
if (excludeCount === 0) {
candidates[candidateIndex] = candidate;
return candidate;
}
excludeCount--;
if (excludeCount > 0) {
excludeArgument![excludeArgument!.indexOf(/*value*/ true)] = false;
}
else {
excludeArgument = undefined;
inferenceContext = createInferenceContext(candidate.typeParameters, candidate, /*flags*/ isInJavaScriptFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None);
typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext);
}
checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJavaScriptFile(candidate.declaration));
// If the original signature has a generic rest type, instantiation may produce a
// signature with different arity and we need to perform another arity check.
if (getGenericRestType(candidate) && !hasCorrectArity(node, args, checkCandidate, signatureHelpTrailingComma)) {
candidateForArgumentArityError = checkCandidate;
continue;
}
}
else {
checkCandidate = candidate;
}
if (!checkApplicableSignature(node, args, checkCandidate, relation, excludeArgument, /*reportErrors*/ false)) {
candidateForArgumentError = checkCandidate;
continue;
}
if (excludeArgument) {
// If one or more context sensitive arguments were excluded, we start including
// them now (and keeping do so for any subsequent candidates) and perform a second
// round of type inference and applicability checking for this particular candidate.
excludeArgument = undefined;
if (inferenceContext) {
const typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext);
checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJavaScriptFile(candidate.declaration));
}
if (!checkApplicableSignature(node, args, checkCandidate, relation, excludeArgument, /*reportErrors*/ false)) {
candidateForArgumentError = checkCandidate;
continue;
}
}
candidates[candidateIndex] = checkCandidate;
return checkCandidate;
}
return undefined;
@ -19877,7 +19610,7 @@ namespace ts {
return signatures.length && every(signatures, signature =>
signature.minArgumentCount === 0 &&
!signature.hasRestParameter &&
signature.parameters.length < getEffectiveArgumentCount(decorator, /*args*/ undefined!, signature));
signature.parameters.length < getDecoratorArgumentCount(decorator, signature));
}
/**
@ -27613,6 +27346,23 @@ namespace ts {
: getDeclaredTypeOfSymbol(classSymbol);
}
function getClassElementPropertyKeyType(element: ClassElement) {
const name = element.name!;
switch (name.kind) {
case SyntaxKind.Identifier:
return getLiteralType(idText(name));
case SyntaxKind.NumericLiteral:
case SyntaxKind.StringLiteral:
return getLiteralType(name.text);
case SyntaxKind.ComputedPropertyName:
const nameType = checkComputedPropertyName(name);
return isTypeAssignableToKind(nameType, TypeFlags.ESSymbolLike) ? nameType : stringType;
default:
Debug.fail("Unsupported property name.");
return errorType;
}
}
// Return the list of properties of the given type, augmented with properties from Function
// if the type has call or construct signatures
function getAugmentedPropertiesOfType(type: Type): Symbol[] {

View file

@ -45,5 +45,7 @@ g("", x => null, x => x.toLowerCase());
>g : Symbol(g, Decl(fixingTypeParametersRepeatedly1.ts, 1, 39), Decl(fixingTypeParametersRepeatedly1.ts, 4, 63))
>x : Symbol(x, Decl(fixingTypeParametersRepeatedly1.ts, 6, 5))
>x : Symbol(x, Decl(fixingTypeParametersRepeatedly1.ts, 6, 16))
>x.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(fixingTypeParametersRepeatedly1.ts, 6, 16))
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))

View file

@ -40,10 +40,10 @@ g("", x => null, x => x.toLowerCase());
>x => null : (x: string) => any
>x : string
>null : null
>x => x.toLowerCase() : (x: any) => any
>x : any
>x.toLowerCase() : any
>x.toLowerCase : any
>x : any
>toLowerCase : any
>x => x.toLowerCase() : (x: string) => string
>x : string
>x.toLowerCase() : string
>x.toLowerCase : () => string
>x : string
>toLowerCase : () => string

View file

@ -189,9 +189,9 @@ var k = fun((Math.random() < 0.5 ? (x => x) : (x => undefined)), x => x, 10);
>x => undefined : (x: number) => any
>x : number
>undefined : undefined
>x => x : (x: any) => any
>x : any
>x : any
>x => x : (x: number) => number
>x : number
>x : number
>10 : 10
var l = fun(((Math.random() < 0.5 ? ((x => x)) : ((x => undefined)))), ((x => x)), 10);
@ -217,11 +217,11 @@ var l = fun(((Math.random() < 0.5 ? ((x => x)) : ((x => undefined)))), ((x => x)
>x => undefined : (x: number) => any
>x : number
>undefined : undefined
>((x => x)) : (x: any) => any
>(x => x) : (x: any) => any
>x => x : (x: any) => any
>x : any
>x : any
>((x => x)) : (x: number) => number
>(x => x) : (x: number) => number
>x => x : (x: number) => number
>x : number
>x : number
>10 : 10
var lambda1: (x: number) => number = x => x;