diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1b34952f3a..03a3f7efa9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25,12 +25,12 @@ namespace ts { // Cancellation that controls whether or not we can cancel in the middle of type checking. // In general cancelling is *not* safe for the type checker. We might be in the middle of // computing something, and we will leave our internals in an inconsistent state. Callers - // who set the cancellation token should catch if a cancellation exception occurs, and + // who set the cancellation token should catch if a cancellation exception occurs, and // should throw away and create a new TypeChecker. // // Currently we only support setting the cancellation token when getting diagnostics. This // is because diagnostics can be quite expensive, and we want to allow hosts to bail out if - // they no longer need the information (for example, if the user started editing again). + // they no longer need the information (for example, if the user started editing again). let cancellationToken: CancellationToken; let Symbol = objectAllocator.getSymbolConstructor(); @@ -117,7 +117,7 @@ namespace ts { let globals: SymbolTable = {}; let globalESSymbolConstructorSymbol: Symbol; - + let getGlobalPromiseConstructorSymbol: () => Symbol; let globalObjectType: ObjectType; @@ -148,7 +148,7 @@ namespace ts { let getInstantiatedGlobalPromiseLikeType: () => ObjectType; let getGlobalPromiseConstructorLikeType: () => ObjectType; let getGlobalThenableType: () => ObjectType; - + let tupleTypes: Map = {}; let unionTypes: Map = {}; let intersectionTypes: Map = {}; @@ -158,7 +158,7 @@ namespace ts { let emitParam = false; let emitAwaiter = false; let emitGenerator = false; - + let resolutionTargets: Object[] = []; let resolutionResults: boolean[] = []; @@ -406,7 +406,7 @@ namespace ts { let moduleExports = getSymbolOfNode(location).exports; if (location.kind === SyntaxKind.SourceFile || (location.kind === SyntaxKind.ModuleDeclaration && (location).name.kind === SyntaxKind.StringLiteral)) { - + // It's an external module. Because of module/namespace merging, a module's exports are in scope, // yet we never want to treat an export specifier as putting a member in scope. Therefore, // if the name we find is purely an export specifier, it is not actually considered in scope. @@ -526,7 +526,7 @@ namespace ts { } break; case SyntaxKind.Decorator: - // Decorators are resolved at the class declaration. Resolving at the parameter + // Decorators are resolved at the class declaration. Resolving at the parameter // or member would result in looking up locals in the method. // // function y() {} @@ -1948,7 +1948,19 @@ namespace ts { writePunctuation(writer, SyntaxKind.ColonToken); } writeSpace(writer); - buildTypeDisplay(getReturnTypeOfSignature(signature), writer, enclosingDeclaration, flags, symbolStack); + + let returnType: Type; + if (signature.typePredicate) { + writer.writeParameter(signature.typePredicate.parameterName); + writeSpace(writer); + writeKeyword(writer, SyntaxKind.IsKeyword); + writeSpace(writer); + returnType = signature.typePredicate.type; + } + else { + returnType = getReturnTypeOfSignature(signature); + } + buildTypeDisplay(returnType, writer, enclosingDeclaration, flags, symbolStack); } function buildSignatureDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { @@ -2098,7 +2110,7 @@ namespace ts { case SyntaxKind.ParenthesizedType: return isDeclarationVisible(node.parent); - // Default binding, import specifier and namespace import is visible + // Default binding, import specifier and namespace import is visible // only on demand so by default it is not visible case SyntaxKind.ImportClause: case SyntaxKind.NamespaceImport: @@ -3864,7 +3876,7 @@ namespace ts { function getGlobalType(name: string, arity = 0): ObjectType { return getTypeOfGlobalSymbol(getGlobalTypeSymbol(name), arity); } - + function tryGetGlobalType(name: string, arity = 0): ObjectType { return getTypeOfGlobalSymbol(getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined), arity); } @@ -3892,7 +3904,7 @@ namespace ts { ? createTypeReference(globalTypedPropertyDescriptorType, [propertyType]) : emptyObjectType; } - + /** * Instantiates a global type that is generic with some element type, and returns that instantiation. */ @@ -4117,7 +4129,7 @@ namespace ts { } return links.resolvedType; } - + function getTypeFromTypeNode(node: TypeNode): Type { switch (node.kind) { case SyntaxKind.AnyKeyword: @@ -6053,7 +6065,7 @@ namespace ts { error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_arrow_function_in_ES3_and_ES5_Consider_using_a_standard_function_expression); } } - + if (node.parserContextFlags & ParserContextFlags.Await) { getNodeLinks(container).flags |= NodeCheckFlags.CaptureArguments; getNodeLinks(node).flags |= NodeCheckFlags.LexicalArguments; @@ -6746,7 +6758,7 @@ namespace ts { // c is represented in the tree as a spread element in an array literal. // But c really functions as a rest element, and its purpose is to provide // a contextual type for the right hand side of the assignment. Therefore, - // instead of calling checkExpression on "...c", which will give an error + // instead of calling checkExpression on "...c", which will give an error // if c is not iterable/array-like, we need to act as if we are trying to // get the contextual element type from it. So we do something similar to // getContextualTypeForElementExpression, which will crucially not error @@ -7399,7 +7411,7 @@ namespace ts { if (flags & NodeFlags.Abstract) { // A method cannot be accessed in a super property access if the method is abstract. - // This error could mask a private property access error. But, a member + // This error could mask a private property access error. But, a member // cannot simultaneously be private and abstract, so this will trigger an // additional error elsewhere. @@ -7478,7 +7490,7 @@ namespace ts { } return unknownType; } - + getNodeLinks(node).resolvedSymbol = prop; if (prop.parent && prop.parent.flags & SymbolFlags.Class) { @@ -7875,7 +7887,7 @@ namespace ts { let paramType = getTypeAtPosition(signature, i); let argType = getEffectiveArgumentType(node, i, arg); - // If the effective argument type is 'undefined', there is no synthetic type + // 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 @@ -7947,8 +7959,8 @@ namespace ts { // Check spread elements against rest type (from arity check we know spread argument corresponds to a rest parameter) let paramType = getTypeAtPosition(signature, i); let argType = getEffectiveArgumentType(node, i, arg); - - // If the effective argument type is 'undefined', there is no synthetic type + + // 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) { argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors @@ -8001,18 +8013,18 @@ namespace ts { 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 + * 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 + * 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. + * us to match a property decorator. * Otherwise, the argument count is the length of the 'args' array. */ function getEffectiveArgumentCount(node: CallLikeExpression, args: Expression[], signature: Signature) { @@ -8024,7 +8036,7 @@ namespace ts { return 1; case SyntaxKind.PropertyDeclaration: - // A property declaration decorator will have two arguments (see + // A property declaration decorator will have two arguments (see // `PropertyDecorator` in core.d.ts) return 2; @@ -8033,12 +8045,12 @@ namespace ts { case SyntaxKind.SetAccessor: // A method or accessor declaration decorator will have two or three arguments (see // `PropertyDecorator` and `MethodDecorator` in core.d.ts) - // If the method decorator signature only accepts a target and a key, we will only + // 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 + // A parameter declaration decorator will have three arguments (see // `ParameterDecorator` in core.d.ts) return 3; @@ -8048,47 +8060,47 @@ namespace ts { return args.length; } } - + /** * 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, + * 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. + * 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. */ function getEffectiveDecoratorFirstArgumentType(node: Node): Type { // The first argument to a decorator is its `target`. switch (node.kind) { case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: - // For a class decorator, the `target` is the type of the class (e.g. the + // For a class decorator, the `target` is the type of the class (e.g. the // "static" or "constructor" side of the class) let classSymbol = getSymbolOfNode(node); return getTypeOfSymbol(classSymbol); case SyntaxKind.Parameter: - // For a parameter decorator, the `target` is the parent type of the - // parameter's containing method. + // For a parameter decorator, the `target` is the parent type of the + // parameter's containing method. node = node.parent; if (node.kind === SyntaxKind.Constructor) { let classSymbol = getSymbolOfNode(node); return getTypeOfSymbol(classSymbol); } - + // fall-through - + case SyntaxKind.PropertyDeclaration: case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case 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 + // declared "static"; otherwise, it is the "instance"-side type of the // parent of the member. return getParentTypeOfClassElement(node); @@ -8097,19 +8109,19 @@ namespace ts { return unknownType; } } - + /** * 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 + * 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, + * 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 + * 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 + * 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 + * 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. */ @@ -8126,18 +8138,18 @@ namespace ts { // 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. - + // fall-through - + case SyntaxKind.PropertyDeclaration: case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case 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; + // 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. let element = node; @@ -8167,11 +8179,11 @@ namespace ts { return unknownType; } } - + /** * 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 + * If 'node' is a method or accessor, the effective argument type is a * `TypedPropertyDescriptor` instantiated with the type of the member. * Class and property decorators do not have a third effective argument. */ @@ -8204,7 +8216,7 @@ namespace ts { return unknownType; } } - + /** * Returns the effective argument type for the provided argument to a decorator. */ @@ -8222,12 +8234,12 @@ namespace ts { Debug.fail("Decorators should not have a fourth synthetic argument."); return unknownType; } - + /** * Gets the effective argument type for an argument in a call expression. */ function getEffectiveArgumentType(node: CallLikeExpression, argIndex: number, arg: Expression): Type { - // Decorators provide special arguments, a tagged template expression provides + // 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) { @@ -8238,12 +8250,12 @@ namespace ts { } // This is not a synthetic argument, so we return 'undefined' - // to signal that the caller needs to check the argument. + // to signal that the caller needs to check the argument. return undefined; } - + /** - * Gets the effective argument expression for an argument in a call expression. + * Gets the effective argument expression for an argument in a call expression. */ function getEffectiveArgument(node: CallLikeExpression, args: Expression[], argIndex: number) { // For a decorator or the first argument of a tagged template expression we return undefined. @@ -8309,7 +8321,7 @@ namespace ts { // 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 + // 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. let excludeArgument: boolean[]; if (!isDecorator) { @@ -8651,7 +8663,7 @@ namespace ts { return resolveCall(node, callSignatures, candidatesOutArray); } - + /** * Gets the localized diagnostic head message to use for errors when resolving a decorator as a call expression. */ @@ -8797,7 +8809,7 @@ namespace ts { links.type = instantiateType(getTypeOfSymbol(lastOrUndefined(context.parameters)), mapper); } } - + function createPromiseType(promisedType: Type): Type { // creates a `Promise` type where `T` is the promisedType argument let globalPromiseType = getGlobalPromiseType(); @@ -8806,7 +8818,7 @@ namespace ts { promisedType = getAwaitedType(promisedType); return createTypeReference(globalPromiseType, [promisedType]); } - + return emptyObjectType; } @@ -8815,15 +8827,15 @@ namespace ts { if (!func.body) { return unknownType; } - + let isAsync = isAsyncFunctionLike(func); let type: Type; if (func.body.kind !== SyntaxKind.Block) { - type = checkExpressionCached(func.body, contextualMapper); + type = checkExpressionCached(func.body, contextualMapper); if (isAsync) { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so the - // return type of the body should be unwrapped to its awaited type, which we will wrap in + // From within an async function you can return either a non-promise value or a promise. Any + // Promise/A+ compatible implementation will always assimilate any foreign promise, so the + // return type of the body should be unwrapped to its awaited type, which we will wrap in // the native Promise type later in this function. type = checkAwaitedType(type, func, Diagnostics.Return_expression_in_async_function_does_not_have_a_valid_callable_then_member); } @@ -8852,13 +8864,13 @@ namespace ts { error(func, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); return unknownType; } - + return promiseType; } else { return voidType; } - } + } } // When yield/return statements are contextually typed we allow the return type to be a union type. // Otherwise we require the yield/return expressions to have a best common supertype. @@ -8881,19 +8893,19 @@ namespace ts { if (!contextualSignature) { reportErrorsFromWidening(func, type); } - + let widenedType = getWidenedType(type); if (isAsync) { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so the + // From within an async function you can return either a non-promise value or a promise. Any + // Promise/A+ compatible implementation will always assimilate any foreign promise, so the // return type of the body is awaited type of the body, wrapped in a native Promise type. let promiseType = createPromiseType(widenedType); if (promiseType === emptyObjectType) { error(func, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); return unknownType; } - - return promiseType; + + return promiseType; } else { return widenedType; @@ -8928,13 +8940,13 @@ namespace ts { forEachReturnStatement(body, returnStatement => { let expr = returnStatement.expression; if (expr) { - let type = checkExpressionCached(expr, contextualMapper); + let type = checkExpressionCached(expr, contextualMapper); if (isAsync) { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so the - // return type of the body should be unwrapped to its awaited type, which should be wrapped in + // From within an async function you can return either a non-promise value or a promise. Any + // Promise/A+ compatible implementation will always assimilate any foreign promise, so the + // return type of the body should be unwrapped to its awaited type, which should be wrapped in // the native Promise type by the caller. - type = checkAwaitedType(type, body.parent, Diagnostics.Return_expression_in_async_function_does_not_have_a_valid_callable_then_member); + type = checkAwaitedType(type, body.parent, Diagnostics.Return_expression_in_async_function_does_not_have_a_valid_callable_then_member); } if (!contains(aggregatedTypes, type)) { @@ -9006,12 +9018,12 @@ namespace ts { if (contextualMapper === identityMapper && isContextSensitive(node)) { return anyFunctionType; } - + let isAsync = isAsyncFunctionLike(node); if (isAsync) { emitAwaiter = true; } - + let links = getNodeLinks(node); let type = getTypeOfSymbol(node.symbol); // Check if function expression is contextually typed and assign parameter types if so @@ -9048,7 +9060,7 @@ namespace ts { function checkFunctionExpressionOrObjectLiteralMethodBody(node: FunctionExpression | MethodDeclaration) { Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); - + let isAsync = isAsyncFunctionLike(node); if (isAsync) { emitAwaiter = true; @@ -9059,7 +9071,7 @@ namespace ts { if (returnType && isAsync) { promisedType = checkAsyncFunctionReturnType(node); } - + if (returnType && !node.asteriskToken) { checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, isAsync ? promisedType : returnType); } @@ -9078,10 +9090,10 @@ namespace ts { checkSourceElement(node.body); } else { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so we - // should not be checking assignability of a promise to the return type. Instead, we need to - // check assignability of the awaited type of the expression body against the promised type of + // From within an async function you can return either a non-promise value or a promise. Any + // Promise/A+ compatible implementation will always assimilate any foreign promise, so we + // should not be checking assignability of a promise to the return type. Instead, we need to + // check assignability of the awaited type of the expression body against the promised type of // its return type annotation. let exprType = checkExpression(node.body); if (returnType) { @@ -9093,7 +9105,7 @@ namespace ts { checkTypeAssignableTo(exprType, returnType, node.body); } } - + checkFunctionAndClassExpressionBodies(node.body); } } @@ -9215,7 +9227,7 @@ namespace ts { let operandType = checkExpression(node.expression); return checkAwaitedType(operandType, node); } - + function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type { let operandType = checkExpression(node.operand); switch (node.operator) { @@ -9688,7 +9700,7 @@ namespace ts { node.contextualType = saveContextualType; return result; } - + function checkExpressionCached(node: Expression, contextualMapper?: TypeMapper): Type { let links = getNodeLinks(node); if (!links.resolvedType) { @@ -9895,7 +9907,7 @@ namespace ts { if (node.questionToken && isBindingPattern(node.name) && func.body) { error(node, Diagnostics.A_binding_pattern_parameter_cannot_be_optional_in_an_implementation_signature); } - + // Only check rest parameter type if it's not a binding pattern. Since binding patterns are // not allowed in a rest parameter, we already have an error from checkGrammarParameterList. if (node.dotDotDotToken && !isBindingPattern(node.name) && !isArrayType(getTypeOfSymbol(node.symbol))) { @@ -10113,7 +10125,7 @@ namespace ts { // Grammar checking for modifiers is done inside the function checkGrammarFunctionLikeDeclaration checkFunctionLikeDeclaration(node); - + // Abstract methods cannot have an implementation. // Extra checks are to avoid reporting multiple errors relating to the "abstractness" of the node. if(node.flags & NodeFlags.Abstract && node.body) { @@ -10548,7 +10560,7 @@ namespace ts { } // Abstract methods can't have an implementation -- in particular, they don't need one. - if (!isExportSymbolInsideModule && lastSeenNonAmbientDeclaration && !lastSeenNonAmbientDeclaration.body && + if (!isExportSymbolInsideModule && lastSeenNonAmbientDeclaration && !lastSeenNonAmbientDeclaration.body && !(lastSeenNonAmbientDeclaration.flags & NodeFlags.Abstract) ) { reportImplementationExpectedError(lastSeenNonAmbientDeclaration); } @@ -10666,13 +10678,13 @@ namespace ts { if (!message) { message = Diagnostics.Operand_for_await_does_not_have_a_valid_callable_then_member; } - + error(location, message); } - + return unknownType; } - + return type; } @@ -10690,16 +10702,16 @@ namespace ts { // ) => any // ): any; // } - // - + // + if (promise.flags & TypeFlags.Any) { return undefined; } - + if ((promise.flags & TypeFlags.Reference) && (promise).target === tryGetGlobalPromiseType()) { return (promise).typeArguments[0]; } - + let globalPromiseLikeType = getInstantiatedGlobalPromiseLikeType(); if (globalPromiseLikeType === emptyObjectType || !isTypeAssignableTo(promise, globalPromiseLikeType)) { return undefined; @@ -10709,58 +10721,58 @@ namespace ts { if (thenFunction && (thenFunction.flags & TypeFlags.Any)) { return undefined; } - + let thenSignatures = thenFunction ? getSignaturesOfType(thenFunction, SignatureKind.Call) : emptyArray; if (thenSignatures.length === 0) { return undefined; } - + let onfulfilledParameterType = getUnionType(map(thenSignatures, getTypeOfFirstParameterOfSignature)); if (onfulfilledParameterType.flags & TypeFlags.Any) { return undefined; } - + let onfulfilledParameterSignatures = getSignaturesOfType(onfulfilledParameterType, SignatureKind.Call); if (onfulfilledParameterSignatures.length === 0) { return undefined; } - + let valueParameterType = getUnionType(map(onfulfilledParameterSignatures, getTypeOfFirstParameterOfSignature)); return valueParameterType; } - + function getTypeOfFirstParameterOfSignature(signature: Signature) { return getTypeAtPosition(signature, 0); } - + /** * Gets the "awaited type" of a type. * @param type The type to await. - * @remarks The "awaited type" of an expression is its "promised type" if the expression is a + * @remarks The "awaited type" of an expression is its "promised type" if the expression is a * Promise-like type; otherwise, it is the type of the expression. This is used to reflect * The runtime behavior of the `await` keyword. */ function getAwaitedType(type: Type) { return checkAwaitedType(type, /*location*/ undefined, /*message*/ undefined); } - + function checkAwaitedType(type: Type, location?: Node, message?: DiagnosticMessage) { return checkAwaitedTypeWorker(type); - + function checkAwaitedTypeWorker(type: Type): Type { if (type.flags & TypeFlags.Union) { let types: Type[] = []; for (let constituentType of (type).types) { types.push(checkAwaitedTypeWorker(constituentType)); } - + return getUnionType(types); } else { let promisedType = getPromisedType(type); if (promisedType === undefined) { // The type was not a PromiseLike, so it could not be unwrapped any further. - // As long as the type does not have a callable "then" property, it is + // As long as the type does not have a callable "then" property, it is // safe to return the type; otherwise, an error will have been reported in // the call to checkNonThenableType and we will return unknownType. // @@ -10771,7 +10783,7 @@ namespace ts { // The "thenable" does not match the minimal definition for a PromiseLike. When // a Promise/A+-compatible or ES6 promise tries to adopt this value, the promise // will never settle. We treat this as an error to help flag an early indicator - // of a runtime problem. If the user wants to return this value from an async + // of a runtime problem. If the user wants to return this value from an async // function, they would need to wrap it in some other value. If they want it to // be treated as a promise, they can cast to . return checkNonThenableType(type, location, message); @@ -10779,70 +10791,70 @@ namespace ts { else { if (type.id === promisedType.id || awaitedTypeStack.indexOf(promisedType.id) >= 0) { // We have a bad actor in the form of a promise whose promised type is - // the same promise type, or a mutually recursive promise. Return the - // unknown type as we cannot guess the shape. If this were the actual + // the same promise type, or a mutually recursive promise. Return the + // unknown type as we cannot guess the shape. If this were the actual // case in the JavaScript, this Promise would never resolve. // - // An example of a bad actor with a singly-recursive promise type might + // An example of a bad actor with a singly-recursive promise type might // be: // // interface BadPromise { // then( - // onfulfilled: (value: BadPromise) => any, + // onfulfilled: (value: BadPromise) => any, // onrejected: (error: any) => any): BadPromise; // } // - // The above interface will pass the PromiseLike check, and return a - // promised type of `BadPromise`. Since this is a self reference, we + // The above interface will pass the PromiseLike check, and return a + // promised type of `BadPromise`. Since this is a self reference, we // don't want to keep recursing ad infinitum. // - // An example of a bad actor in the form of a mutually-recursive + // An example of a bad actor in the form of a mutually-recursive // promise type might be: // // interface BadPromiseA { // then( - // onfulfilled: (value: BadPromiseB) => any, + // onfulfilled: (value: BadPromiseB) => any, // onrejected: (error: any) => any): BadPromiseB; // } // // interface BadPromiseB { // then( - // onfulfilled: (value: BadPromiseA) => any, + // onfulfilled: (value: BadPromiseA) => any, // onrejected: (error: any) => any): BadPromiseA; // } // if (location) { error( - location, - Diagnostics._0_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method, + location, + Diagnostics._0_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method, symbolToString(type.symbol)); } - + return unknownType; } - + // Keep track of the type we're about to unwrap to avoid bad recursive promise types. // See the comments above for more information. awaitedTypeStack.push(type.id); let awaitedType = checkAwaitedTypeWorker(promisedType); awaitedTypeStack.pop(); - return awaitedType; + return awaitedType; } } } } /** - * Checks the return type of an async function to ensure it is a compatible + * Checks the return type of an async function to ensure it is a compatible * Promise implementation. * @param node The signature to check * @param returnType The return type for the function - * @remarks - * This checks that an async function has a valid Promise-compatible return type, - * and returns the *awaited type* of the promise. An async function has a valid - * Promise-compatible return type if the resolved value of the return type has a + * @remarks + * This checks that an async function has a valid Promise-compatible return type, + * and returns the *awaited type* of the promise. An async function has a valid + * Promise-compatible return type if the resolved value of the return type has a * construct signature that takes in an `initializer` function that in turn supplies - * a `resolve` function as one of its arguments and results in an object with a + * a `resolve` function as one of its arguments and results in an object with a * callable `then` signature. */ function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration): Type { @@ -10855,7 +10867,7 @@ namespace ts { // As part of our emit for an async function, we will need to emit the entity name of // the return type annotation as an expression. To meet the necessary runtime semantics - // for __awaiter, we must also check that the type of the declaration (e.g. the static + // for __awaiter, we must also check that the type of the declaration (e.g. the static // side or "constructor" of the promise type) is compatible `PromiseConstructorLike`. // // An example might be (from lib.es6.d.ts): @@ -10863,11 +10875,11 @@ namespace ts { // interface Promise { ... } // interface PromiseConstructor { // new (...): Promise; - // } + // } // declare var Promise: PromiseConstructor; // - // When an async function declares a return type annotation of `Promise`, we - // need to get the type of the `Promise` variable declaration above, which would + // When an async function declares a return type annotation of `Promise`, we + // need to get the type of the `Promise` variable declaration above, which would // be `PromiseConstructor`. // // The same case applies to a class: @@ -10879,20 +10891,20 @@ namespace ts { // // When we get the type of the `Promise` symbol here, we get the type of the static // side of the `Promise` class, which would be `{ new (...): Promise }`. - + let promiseType = getTypeFromTypeNode(node.type); if (promiseType === unknownType && compilerOptions.isolatedModules) { - // If we are compiling with isolatedModules, we may not be able to resolve the + // If we are compiling with isolatedModules, we may not be able to resolve the // type as a value. As such, we will just return unknownType; return unknownType; } - + let promiseConstructor = getMergedSymbol(promiseType.symbol); if (!promiseConstructor || !symbolIsValue(promiseConstructor)) { error(node, Diagnostics.Type_0_is_not_a_valid_async_function_return_type, typeToString(promiseType)); return unknownType } - + // Validate the promise constructor type. let promiseConstructorType = getTypeOfSymbol(promiseConstructor); if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, node, Diagnostics.Type_0_is_not_a_valid_async_function_return_type)) { @@ -10913,7 +10925,7 @@ namespace ts { // Get and return the awaited type of the return type. return checkAwaitedType(promiseType, node, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); } - + /** Check a decorator */ function checkDecorator(node: Decorator): void { let signature = getResolvedSignature(node); @@ -10963,11 +10975,11 @@ namespace ts { headMessage, errorInfo); } - + /** Checks a type reference node as an expression. */ function checkTypeNodeAsExpression(node: TypeNode) { // When we are emitting type metadata for decorators, we need to try to check the type - // as if it were an expression so that we can emit the type in a value position when we + // as if it were an expression so that we can emit the type in a value position when we // serialize the type metadata. if (node && node.kind === SyntaxKind.TypeReference) { let type = getTypeFromTypeNode(node); @@ -10982,7 +10994,7 @@ namespace ts { } /** - * Checks the type annotation of an accessor declaration or property declaration as + * Checks the type annotation of an accessor declaration or property declaration as * an expression if it is a type reference to a type with a value declaration. */ function checkTypeAnnotationAsExpression(node: AccessorDeclaration | PropertyDeclaration | ParameterDeclaration | MethodDeclaration) { @@ -11004,7 +11016,7 @@ namespace ts { break; } } - + /** Checks the type annotation of the parameters of a function/method or the constructor of a class as expressions */ function checkParameterTypeAnnotationsAsExpressions(node: FunctionLikeDeclaration) { // ensure all type annotations with a value declaration are checked as an expression @@ -11078,9 +11090,9 @@ namespace ts { if (!compilerOptions.experimentalAsyncFunctions) { error(node, Diagnostics.Experimental_support_for_async_functions_is_a_feature_that_is_subject_to_change_in_a_future_release_Specify_experimentalAsyncFunctions_to_remove_this_warning); } - + emitAwaiter = true; - } + } // Do not use hasDynamicName here, because that returns false for well known symbols. // We want to perform checkComputedPropertyName for all computed properties, including @@ -11120,7 +11132,7 @@ namespace ts { if (isAsync) { promisedType = checkAsyncFunctionReturnType(node); } - + checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, isAsync ? promisedType : returnType); } @@ -11332,7 +11344,7 @@ namespace ts { } } } - + // Check that a parameter initializer contains no references to parameters declared to the right of itself function checkParameterInitializer(node: VariableLikeDeclaration): void { if (getRootDeclaration(node).kind !== SyntaxKind.Parameter) { @@ -11653,7 +11665,7 @@ namespace ts { return elementType || anyType; } - + /** * We want to treat type as an iterable, and get the type it is an iterable of. The iterable * must have the following structure (annotated with the names of the variables below): @@ -12280,20 +12292,20 @@ namespace ts { // In order to resolve whether the inherited method was overriden in the base class or not, // we compare the Symbols obtained. Since getTargetSymbol returns the symbol on the *uninstantiated* // type declaration, derived and base resolve to the same symbol even in the case of generic classes. - if (derived === base) { + if (derived === base) { // derived class inherits base without override/redeclaration let derivedClassDecl = getDeclarationOfKind(type.symbol, SyntaxKind.ClassDeclaration); // It is an error to inherit an abstract member without implementing it or being declared abstract. - // If there is no declaration for the derived class (as in the case of class expressions), - // then the class cannot be declared abstract. + // If there is no declaration for the derived class (as in the case of class expressions), + // then the class cannot be declared abstract. if ( baseDeclarationFlags & NodeFlags.Abstract && (!derivedClassDecl || !(derivedClassDecl.flags & NodeFlags.Abstract))) { error(derivedClassDecl, Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2, typeToString(type), symbolToString(baseProperty), typeToString(baseType)); } } - else { + else { // derived overrides base. let derivedDeclarationFlags = getDeclarationFlagsFromSymbol(derived); if ((baseDeclarationFlags & NodeFlags.Private) || (derivedDeclarationFlags & NodeFlags.Private)) { @@ -12764,7 +12776,7 @@ namespace ts { } } - // if the module merges with a class declaration in the same lexical scope, + // if the module merges with a class declaration in the same lexical scope, // we need to track this to ensure the correct emit. let mergedClass = getDeclarationOfKind(symbol, SyntaxKind.ClassDeclaration); if (mergedClass && @@ -13298,7 +13310,7 @@ namespace ts { forEach(potentialThisCollisions, checkIfThisIsCapturedInEnclosingScope); potentialThisCollisions.length = 0; } - + if (emitExtends) { links.flags |= NodeCheckFlags.EmitExtends; } @@ -13310,11 +13322,11 @@ namespace ts { if (emitParam) { links.flags |= NodeCheckFlags.EmitParam; } - + if (emitAwaiter) { links.flags |= NodeCheckFlags.EmitAwaiter; } - + if (emitGenerator || (emitAwaiter && languageVersion < ScriptTarget.ES6)) { links.flags |= NodeCheckFlags.EmitGenerator; } @@ -13713,7 +13725,7 @@ namespace ts { } /** - * Gets either the static or instance type of a class element, based on + * Gets either the static or instance type of a class element, based on * whether the element is declared as "static". */ function getParentTypeOfClassElement(node: ClassElement) { @@ -13722,7 +13734,7 @@ namespace ts { ? getTypeOfSymbol(classSymbol) : getDeclaredTypeOfSymbol(classSymbol); } - + // 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[] { @@ -14061,7 +14073,7 @@ namespace ts { // * The serialized type of an AccessorDeclaration is the serialized type of the return type annotation of its getter or parameter type annotation of its setter. // * The serialized type of any other FunctionLikeDeclaration is "Function". // * The serialized type of any other node is "void 0". - // + // // For rules on serializing type annotations, see `serializeTypeNode`. switch (node.kind) { case SyntaxKind.ClassDeclaration: return "Function"; @@ -14075,14 +14087,14 @@ namespace ts { } return "void 0"; } - + /** Serializes the parameter types of a function or the constructor of a class. Used by the __metadata decorator for a method or set accessor. */ function serializeParameterTypesOfNode(node: Node): (string | string[])[] { // serialization of parameter types uses the following rules: // // * If the declaration is a class, the parameters of the first constructor with a body are used. // * If the declaration is function-like and has a body, the parameters of the function are used. - // + // // For the rules on serializing the type of each parameter declaration, see `serializeTypeOfDeclaration`. if (node) { var valueDeclaration: FunctionLikeDeclaration; @@ -14299,21 +14311,21 @@ namespace ts { anyArrayType = createArrayType(anyType); } - + function createInstantiatedPromiseLikeType(): ObjectType { let promiseLikeType = getGlobalPromiseLikeType(); if (promiseLikeType !== emptyObjectType) { return createTypeReference(promiseLikeType, [anyType]); } - + return emptyObjectType; } - + function createThenableType() { // build the thenable type that is used to verify against a non-promise "thenable" operand to `await`. let thenPropertySymbol = createSymbol(SymbolFlags.Transient | SymbolFlags.Property, "then"); getSymbolLinks(thenPropertySymbol).type = globalFunctionType; - + let thenableType = createObjectType(TypeFlags.Anonymous); thenableType.properties = [thenPropertySymbol]; thenableType.members = createSymbolTable(thenableType.properties); diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 3db032b8d0..97c85c70de 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -401,7 +401,7 @@ namespace ts { } function emitTypePredicate(type: TypePredicateNode) { - writeEntityName(type.parameterName); + writeTextOfNode(currentSourceFile, type.parameterName); write(" is "); emitType(type.type); } diff --git a/tests/cases/compiler/declFileFunctions.ts b/tests/cases/compiler/declFileFunctions.ts index 36972d427f..7b9c55e921 100644 --- a/tests/cases/compiler/declFileFunctions.ts +++ b/tests/cases/compiler/declFileFunctions.ts @@ -31,6 +31,15 @@ export function fooWithSingleOverload(a: any) { export function fooWithTypePredicate(a: any): a is number { return true; } +export function fooWithTypePredicateAndMulitpleParams(a: any, b: any, c: any): a is number { + return true; +} +export function fooWithTypeTypePredicateAndGeneric(a: any): a is T { + return true; +} +export function fooWithTypeTypePredicateAndRestParam(a: any, ...rest): a is number { + return true; +} /** This comment should appear for nonExportedFoo*/ function nonExportedFoo() { diff --git a/tests/cases/fourslash/signatureHelpOnTypePredicates.ts b/tests/cases/fourslash/signatureHelpOnTypePredicates.ts new file mode 100644 index 0000000000..bfaa7df250 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpOnTypePredicates.ts @@ -0,0 +1,20 @@ +/// + +//// function f1(a: any): a is number {} +//// function f2(a: any): a is T {} +//// function f3(a: any, ...b): a is number {} +//// f1(/*1*/) +//// f2(/*2*/) +//// f3(/*3*/) + +goTo.marker("1"); +verify.signatureHelpCountIs(1); +verify.currentSignatureHelpIs("f1(a: any): a is number"); + +goTo.marker("2"); +verify.signatureHelpCountIs(1); +verify.currentSignatureHelpIs("f2(a: any): a is T"); + +goTo.marker("3"); +verify.signatureHelpCountIs(1); +verify.currentSignatureHelpIs("f3(a: any, ...b: any[]): a is number"); \ No newline at end of file