Adds type predicate signature display and addresses CR feedback

This commit is contained in:
Tingan Ho 2015-07-09 11:13:32 +08:00
parent 08a7744254
commit 28976a0e34
4 changed files with 206 additions and 165 deletions

View file

@ -25,12 +25,12 @@ namespace ts {
// Cancellation that controls whether or not we can cancel in the middle of type checking. // 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 // 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 // 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. // should throw away and create a new TypeChecker.
// //
// Currently we only support setting the cancellation token when getting diagnostics. This // 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 // 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 cancellationToken: CancellationToken;
let Symbol = objectAllocator.getSymbolConstructor(); let Symbol = objectAllocator.getSymbolConstructor();
@ -117,7 +117,7 @@ namespace ts {
let globals: SymbolTable = {}; let globals: SymbolTable = {};
let globalESSymbolConstructorSymbol: Symbol; let globalESSymbolConstructorSymbol: Symbol;
let getGlobalPromiseConstructorSymbol: () => Symbol; let getGlobalPromiseConstructorSymbol: () => Symbol;
let globalObjectType: ObjectType; let globalObjectType: ObjectType;
@ -148,7 +148,7 @@ namespace ts {
let getInstantiatedGlobalPromiseLikeType: () => ObjectType; let getInstantiatedGlobalPromiseLikeType: () => ObjectType;
let getGlobalPromiseConstructorLikeType: () => ObjectType; let getGlobalPromiseConstructorLikeType: () => ObjectType;
let getGlobalThenableType: () => ObjectType; let getGlobalThenableType: () => ObjectType;
let tupleTypes: Map<TupleType> = {}; let tupleTypes: Map<TupleType> = {};
let unionTypes: Map<UnionType> = {}; let unionTypes: Map<UnionType> = {};
let intersectionTypes: Map<IntersectionType> = {}; let intersectionTypes: Map<IntersectionType> = {};
@ -158,7 +158,7 @@ namespace ts {
let emitParam = false; let emitParam = false;
let emitAwaiter = false; let emitAwaiter = false;
let emitGenerator = false; let emitGenerator = false;
let resolutionTargets: Object[] = []; let resolutionTargets: Object[] = [];
let resolutionResults: boolean[] = []; let resolutionResults: boolean[] = [];
@ -406,7 +406,7 @@ namespace ts {
let moduleExports = getSymbolOfNode(location).exports; let moduleExports = getSymbolOfNode(location).exports;
if (location.kind === SyntaxKind.SourceFile || if (location.kind === SyntaxKind.SourceFile ||
(location.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>location).name.kind === SyntaxKind.StringLiteral)) { (location.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>location).name.kind === SyntaxKind.StringLiteral)) {
// It's an external module. Because of module/namespace merging, a module's exports are in scope, // 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, // 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. // if the name we find is purely an export specifier, it is not actually considered in scope.
@ -526,7 +526,7 @@ namespace ts {
} }
break; break;
case SyntaxKind.Decorator: 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. // or member would result in looking up locals in the method.
// //
// function y() {} // function y() {}
@ -1948,7 +1948,19 @@ namespace ts {
writePunctuation(writer, SyntaxKind.ColonToken); writePunctuation(writer, SyntaxKind.ColonToken);
} }
writeSpace(writer); 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[]) { function buildSignatureDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
@ -2098,7 +2110,7 @@ namespace ts {
case SyntaxKind.ParenthesizedType: case SyntaxKind.ParenthesizedType:
return isDeclarationVisible(<Declaration>node.parent); return isDeclarationVisible(<Declaration>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 // only on demand so by default it is not visible
case SyntaxKind.ImportClause: case SyntaxKind.ImportClause:
case SyntaxKind.NamespaceImport: case SyntaxKind.NamespaceImport:
@ -3864,7 +3876,7 @@ namespace ts {
function getGlobalType(name: string, arity = 0): ObjectType { function getGlobalType(name: string, arity = 0): ObjectType {
return getTypeOfGlobalSymbol(getGlobalTypeSymbol(name), arity); return getTypeOfGlobalSymbol(getGlobalTypeSymbol(name), arity);
} }
function tryGetGlobalType(name: string, arity = 0): ObjectType { function tryGetGlobalType(name: string, arity = 0): ObjectType {
return getTypeOfGlobalSymbol(getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined), arity); return getTypeOfGlobalSymbol(getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined), arity);
} }
@ -3892,7 +3904,7 @@ namespace ts {
? createTypeReference(<GenericType>globalTypedPropertyDescriptorType, [propertyType]) ? createTypeReference(<GenericType>globalTypedPropertyDescriptorType, [propertyType])
: emptyObjectType; : emptyObjectType;
} }
/** /**
* Instantiates a global type that is generic with some element type, and returns that instantiation. * Instantiates a global type that is generic with some element type, and returns that instantiation.
*/ */
@ -4117,7 +4129,7 @@ namespace ts {
} }
return links.resolvedType; return links.resolvedType;
} }
function getTypeFromTypeNode(node: TypeNode): Type { function getTypeFromTypeNode(node: TypeNode): Type {
switch (node.kind) { switch (node.kind) {
case SyntaxKind.AnyKeyword: 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); 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) { if (node.parserContextFlags & ParserContextFlags.Await) {
getNodeLinks(container).flags |= NodeCheckFlags.CaptureArguments; getNodeLinks(container).flags |= NodeCheckFlags.CaptureArguments;
getNodeLinks(node).flags |= NodeCheckFlags.LexicalArguments; 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. // 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 // 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, // 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 // 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 // get the contextual element type from it. So we do something similar to
// getContextualTypeForElementExpression, which will crucially not error // getContextualTypeForElementExpression, which will crucially not error
@ -7399,7 +7411,7 @@ namespace ts {
if (flags & NodeFlags.Abstract) { if (flags & NodeFlags.Abstract) {
// A method cannot be accessed in a super property access if the method is 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 // cannot simultaneously be private and abstract, so this will trigger an
// additional error elsewhere. // additional error elsewhere.
@ -7478,7 +7490,7 @@ namespace ts {
} }
return unknownType; return unknownType;
} }
getNodeLinks(node).resolvedSymbol = prop; getNodeLinks(node).resolvedSymbol = prop;
if (prop.parent && prop.parent.flags & SymbolFlags.Class) { if (prop.parent && prop.parent.flags & SymbolFlags.Class) {
@ -7875,7 +7887,7 @@ namespace ts {
let paramType = getTypeAtPosition(signature, i); let paramType = getTypeAtPosition(signature, i);
let argType = getEffectiveArgumentType(node, i, arg); 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. // for the argument. In that case, we should check the argument.
if (argType === undefined) { if (argType === undefined) {
// For context sensitive arguments we pass the identityMapper, which is a signal to treat all // 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) // Check spread elements against rest type (from arity check we know spread argument corresponds to a rest parameter)
let paramType = getTypeAtPosition(signature, i); let paramType = getTypeAtPosition(signature, i);
let argType = getEffectiveArgumentType(node, i, arg); 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. // for the argument. In that case, we should check the argument.
if (argType === undefined) { if (argType === undefined) {
argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors
@ -8001,18 +8013,18 @@ namespace ts {
return args; return args;
} }
/** /**
* Returns the effective argument count for a node that works like a function invocation. * 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 * If 'node' is a Decorator, the number of arguments is derived from the decoration
* target and the signature: * 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. * count is 1.
* If 'node.target' is a parameter declaration, the effective argument count is 3. * 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 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 * 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. * Otherwise, the argument count is the length of the 'args' array.
*/ */
function getEffectiveArgumentCount(node: CallLikeExpression, args: Expression[], signature: Signature) { function getEffectiveArgumentCount(node: CallLikeExpression, args: Expression[], signature: Signature) {
@ -8024,7 +8036,7 @@ namespace ts {
return 1; return 1;
case SyntaxKind.PropertyDeclaration: 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) // `PropertyDecorator` in core.d.ts)
return 2; return 2;
@ -8033,12 +8045,12 @@ namespace ts {
case SyntaxKind.SetAccessor: case SyntaxKind.SetAccessor:
// A method or accessor declaration decorator will have two or three arguments (see // A method or accessor declaration decorator will have two or three arguments (see
// `PropertyDecorator` and `MethodDecorator` in core.d.ts) // `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. // type check those arguments.
return signature.parameters.length >= 3 ? 3 : 2; return signature.parameters.length >= 3 ? 3 : 2;
case SyntaxKind.Parameter: 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) // `ParameterDecorator` in core.d.ts)
return 3; return 3;
@ -8048,47 +8060,47 @@ namespace ts {
return args.length; return args.length;
} }
} }
/** /**
* Returns the effective type of the first argument to a decorator. * Returns the effective type of the first argument to a decorator.
* If 'node' is a class declaration or class expression, the effective argument type * If 'node' is a class declaration or class expression, the effective argument type
* is the type of the static side of the class. * is the type of the static side of the class.
* If 'node' is a parameter declaration, the effective argument type is either the type * 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. * depending on whether the method is declared static.
* For a constructor, the type is always the type of the static side of the class. * 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 * 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 * 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. * element, depending on whether the element is declared static.
*/ */
function getEffectiveDecoratorFirstArgumentType(node: Node): Type { function getEffectiveDecoratorFirstArgumentType(node: Node): Type {
// The first argument to a decorator is its `target`. // The first argument to a decorator is its `target`.
switch (node.kind) { switch (node.kind) {
case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression: 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) // "static" or "constructor" side of the class)
let classSymbol = getSymbolOfNode(node); let classSymbol = getSymbolOfNode(node);
return getTypeOfSymbol(classSymbol); return getTypeOfSymbol(classSymbol);
case SyntaxKind.Parameter: case SyntaxKind.Parameter:
// For a parameter decorator, the `target` is the parent type of the // For a parameter decorator, the `target` is the parent type of the
// parameter's containing method. // parameter's containing method.
node = node.parent; node = node.parent;
if (node.kind === SyntaxKind.Constructor) { if (node.kind === SyntaxKind.Constructor) {
let classSymbol = getSymbolOfNode(node); let classSymbol = getSymbolOfNode(node);
return getTypeOfSymbol(classSymbol); return getTypeOfSymbol(classSymbol);
} }
// fall-through // fall-through
case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertyDeclaration:
case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor: case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor: case SyntaxKind.SetAccessor:
// For a property or method decorator, the `target` is the // For a property or method decorator, the `target` is the
// "static"-side type of the parent of the member if the member is // "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. // parent of the member.
return getParentTypeOfClassElement(<ClassElement>node); return getParentTypeOfClassElement(<ClassElement>node);
@ -8097,19 +8109,19 @@ namespace ts {
return unknownType; return unknownType;
} }
} }
/** /**
* Returns the effective type for the second argument to a decorator. * 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' 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`. * 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. * 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. * 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. * 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. * be a symbol type or the string type.
* A class decorator does not have a second argument 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`. // For a constructor parameter decorator, the `propertyKey` will be `undefined`.
return anyType; return anyType;
} }
// For a non-constructor parameter decorator, the `propertyKey` will be either // 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. // a string or a symbol, based on the name of the parameter's containing method.
// fall-through // fall-through
case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertyDeclaration:
case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor: case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor: case SyntaxKind.SetAccessor:
// The `propertyKey` for a property or method decorator will be a // 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 // otherwise, if the member name is a computed property name it will
// be either string or symbol. // be either string or symbol.
let element = <ClassElement>node; let element = <ClassElement>node;
@ -8167,11 +8179,11 @@ namespace ts {
return unknownType; return unknownType;
} }
} }
/** /**
* Returns the effective argument type for the third argument to a decorator. * 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 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<T>` instantiated with the type of the member. * `TypedPropertyDescriptor<T>` instantiated with the type of the member.
* Class and property decorators do not have a third effective argument. * Class and property decorators do not have a third effective argument.
*/ */
@ -8204,7 +8216,7 @@ namespace ts {
return unknownType; return unknownType;
} }
} }
/** /**
* Returns the effective argument type for the provided argument to a decorator. * 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."); Debug.fail("Decorators should not have a fourth synthetic argument.");
return unknownType; return unknownType;
} }
/** /**
* Gets the effective argument type for an argument in a call expression. * Gets the effective argument type for an argument in a call expression.
*/ */
function getEffectiveArgumentType(node: CallLikeExpression, argIndex: number, arg: Expression): Type { 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 // a special first argument, and string literals get string literal types
// unless we're reporting errors // unless we're reporting errors
if (node.kind === SyntaxKind.Decorator) { if (node.kind === SyntaxKind.Decorator) {
@ -8238,12 +8250,12 @@ namespace ts {
} }
// This is not a synthetic argument, so we return 'undefined' // 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; 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) { function getEffectiveArgument(node: CallLikeExpression, args: Expression[], argIndex: number) {
// For a decorator or the first argument of a tagged template expression we return undefined. // 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 // For a tagged template, then the first argument be 'undefined' if necessary
// because it represents a TemplateStringsArray. // 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. // decorators are applied to a declaration by the emitter, and not to an expression.
let excludeArgument: boolean[]; let excludeArgument: boolean[];
if (!isDecorator) { if (!isDecorator) {
@ -8651,7 +8663,7 @@ namespace ts {
return resolveCall(node, callSignatures, candidatesOutArray); return resolveCall(node, callSignatures, candidatesOutArray);
} }
/** /**
* Gets the localized diagnostic head message to use for errors when resolving a decorator as a call expression. * 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); links.type = instantiateType(getTypeOfSymbol(lastOrUndefined(context.parameters)), mapper);
} }
} }
function createPromiseType(promisedType: Type): Type { function createPromiseType(promisedType: Type): Type {
// creates a `Promise<T>` type where `T` is the promisedType argument // creates a `Promise<T>` type where `T` is the promisedType argument
let globalPromiseType = getGlobalPromiseType(); let globalPromiseType = getGlobalPromiseType();
@ -8806,7 +8818,7 @@ namespace ts {
promisedType = getAwaitedType(promisedType); promisedType = getAwaitedType(promisedType);
return createTypeReference(<GenericType>globalPromiseType, [promisedType]); return createTypeReference(<GenericType>globalPromiseType, [promisedType]);
} }
return emptyObjectType; return emptyObjectType;
} }
@ -8815,15 +8827,15 @@ namespace ts {
if (!func.body) { if (!func.body) {
return unknownType; return unknownType;
} }
let isAsync = isAsyncFunctionLike(func); let isAsync = isAsyncFunctionLike(func);
let type: Type; let type: Type;
if (func.body.kind !== SyntaxKind.Block) { if (func.body.kind !== SyntaxKind.Block) {
type = checkExpressionCached(<Expression>func.body, contextualMapper); type = checkExpressionCached(<Expression>func.body, contextualMapper);
if (isAsync) { if (isAsync) {
// From within an async function you can return either a non-promise value or a promise. Any // 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 // 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 // return type of the body should be unwrapped to its awaited type, which we will wrap in
// the native Promise<T> type later in this function. // the native Promise<T> type later in this function.
type = checkAwaitedType(type, func, Diagnostics.Return_expression_in_async_function_does_not_have_a_valid_callable_then_member); 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); error(func, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
return unknownType; return unknownType;
} }
return promiseType; return promiseType;
} }
else { else {
return voidType; return voidType;
} }
} }
} }
// When yield/return statements are contextually typed we allow the return type to be a union type. // 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. // Otherwise we require the yield/return expressions to have a best common supertype.
@ -8881,19 +8893,19 @@ namespace ts {
if (!contextualSignature) { if (!contextualSignature) {
reportErrorsFromWidening(func, type); reportErrorsFromWidening(func, type);
} }
let widenedType = getWidenedType(type); let widenedType = getWidenedType(type);
if (isAsync) { if (isAsync) {
// From within an async function you can return either a non-promise value or a promise. Any // 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 // 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<T> type. // return type of the body is awaited type of the body, wrapped in a native Promise<T> type.
let promiseType = createPromiseType(widenedType); let promiseType = createPromiseType(widenedType);
if (promiseType === emptyObjectType) { if (promiseType === emptyObjectType) {
error(func, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); error(func, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
return unknownType; return unknownType;
} }
return promiseType; return promiseType;
} }
else { else {
return widenedType; return widenedType;
@ -8928,13 +8940,13 @@ namespace ts {
forEachReturnStatement(body, returnStatement => { forEachReturnStatement(body, returnStatement => {
let expr = returnStatement.expression; let expr = returnStatement.expression;
if (expr) { if (expr) {
let type = checkExpressionCached(expr, contextualMapper); let type = checkExpressionCached(expr, contextualMapper);
if (isAsync) { if (isAsync) {
// From within an async function you can return either a non-promise value or a promise. Any // 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 // 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 // return type of the body should be unwrapped to its awaited type, which should be wrapped in
// the native Promise<T> type by the caller. // the native Promise<T> 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)) { if (!contains(aggregatedTypes, type)) {
@ -9006,12 +9018,12 @@ namespace ts {
if (contextualMapper === identityMapper && isContextSensitive(node)) { if (contextualMapper === identityMapper && isContextSensitive(node)) {
return anyFunctionType; return anyFunctionType;
} }
let isAsync = isAsyncFunctionLike(node); let isAsync = isAsyncFunctionLike(node);
if (isAsync) { if (isAsync) {
emitAwaiter = true; emitAwaiter = true;
} }
let links = getNodeLinks(node); let links = getNodeLinks(node);
let type = getTypeOfSymbol(node.symbol); let type = getTypeOfSymbol(node.symbol);
// Check if function expression is contextually typed and assign parameter types if so // Check if function expression is contextually typed and assign parameter types if so
@ -9048,7 +9060,7 @@ namespace ts {
function checkFunctionExpressionOrObjectLiteralMethodBody(node: FunctionExpression | MethodDeclaration) { function checkFunctionExpressionOrObjectLiteralMethodBody(node: FunctionExpression | MethodDeclaration) {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
let isAsync = isAsyncFunctionLike(node); let isAsync = isAsyncFunctionLike(node);
if (isAsync) { if (isAsync) {
emitAwaiter = true; emitAwaiter = true;
@ -9059,7 +9071,7 @@ namespace ts {
if (returnType && isAsync) { if (returnType && isAsync) {
promisedType = checkAsyncFunctionReturnType(node); promisedType = checkAsyncFunctionReturnType(node);
} }
if (returnType && !node.asteriskToken) { if (returnType && !node.asteriskToken) {
checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, isAsync ? promisedType : returnType); checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, isAsync ? promisedType : returnType);
} }
@ -9078,10 +9090,10 @@ namespace ts {
checkSourceElement(node.body); checkSourceElement(node.body);
} }
else { else {
// From within an async function you can return either a non-promise value or a promise. Any // 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 // 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 // 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 // check assignability of the awaited type of the expression body against the promised type of
// its return type annotation. // its return type annotation.
let exprType = checkExpression(<Expression>node.body); let exprType = checkExpression(<Expression>node.body);
if (returnType) { if (returnType) {
@ -9093,7 +9105,7 @@ namespace ts {
checkTypeAssignableTo(exprType, returnType, node.body); checkTypeAssignableTo(exprType, returnType, node.body);
} }
} }
checkFunctionAndClassExpressionBodies(node.body); checkFunctionAndClassExpressionBodies(node.body);
} }
} }
@ -9215,7 +9227,7 @@ namespace ts {
let operandType = checkExpression(node.expression); let operandType = checkExpression(node.expression);
return checkAwaitedType(operandType, node); return checkAwaitedType(operandType, node);
} }
function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type { function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type {
let operandType = checkExpression(node.operand); let operandType = checkExpression(node.operand);
switch (node.operator) { switch (node.operator) {
@ -9688,7 +9700,7 @@ namespace ts {
node.contextualType = saveContextualType; node.contextualType = saveContextualType;
return result; return result;
} }
function checkExpressionCached(node: Expression, contextualMapper?: TypeMapper): Type { function checkExpressionCached(node: Expression, contextualMapper?: TypeMapper): Type {
let links = getNodeLinks(node); let links = getNodeLinks(node);
if (!links.resolvedType) { if (!links.resolvedType) {
@ -9895,7 +9907,7 @@ namespace ts {
if (node.questionToken && isBindingPattern(node.name) && func.body) { if (node.questionToken && isBindingPattern(node.name) && func.body) {
error(node, Diagnostics.A_binding_pattern_parameter_cannot_be_optional_in_an_implementation_signature); 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 // 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. // not allowed in a rest parameter, we already have an error from checkGrammarParameterList.
if (node.dotDotDotToken && !isBindingPattern(node.name) && !isArrayType(getTypeOfSymbol(node.symbol))) { 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 // Grammar checking for modifiers is done inside the function checkGrammarFunctionLikeDeclaration
checkFunctionLikeDeclaration(node); checkFunctionLikeDeclaration(node);
// Abstract methods cannot have an implementation. // Abstract methods cannot have an implementation.
// Extra checks are to avoid reporting multiple errors relating to the "abstractness" of the node. // Extra checks are to avoid reporting multiple errors relating to the "abstractness" of the node.
if(node.flags & NodeFlags.Abstract && node.body) { 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. // 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) ) { !(lastSeenNonAmbientDeclaration.flags & NodeFlags.Abstract) ) {
reportImplementationExpectedError(lastSeenNonAmbientDeclaration); reportImplementationExpectedError(lastSeenNonAmbientDeclaration);
} }
@ -10666,13 +10678,13 @@ namespace ts {
if (!message) { if (!message) {
message = Diagnostics.Operand_for_await_does_not_have_a_valid_callable_then_member; message = Diagnostics.Operand_for_await_does_not_have_a_valid_callable_then_member;
} }
error(location, message); error(location, message);
} }
return unknownType; return unknownType;
} }
return type; return type;
} }
@ -10690,16 +10702,16 @@ namespace ts {
// ) => any // ) => any
// ): any; // ): any;
// } // }
// //
if (promise.flags & TypeFlags.Any) { if (promise.flags & TypeFlags.Any) {
return undefined; return undefined;
} }
if ((promise.flags & TypeFlags.Reference) && (<GenericType>promise).target === tryGetGlobalPromiseType()) { if ((promise.flags & TypeFlags.Reference) && (<GenericType>promise).target === tryGetGlobalPromiseType()) {
return (<GenericType>promise).typeArguments[0]; return (<GenericType>promise).typeArguments[0];
} }
let globalPromiseLikeType = getInstantiatedGlobalPromiseLikeType(); let globalPromiseLikeType = getInstantiatedGlobalPromiseLikeType();
if (globalPromiseLikeType === emptyObjectType || !isTypeAssignableTo(promise, globalPromiseLikeType)) { if (globalPromiseLikeType === emptyObjectType || !isTypeAssignableTo(promise, globalPromiseLikeType)) {
return undefined; return undefined;
@ -10709,58 +10721,58 @@ namespace ts {
if (thenFunction && (thenFunction.flags & TypeFlags.Any)) { if (thenFunction && (thenFunction.flags & TypeFlags.Any)) {
return undefined; return undefined;
} }
let thenSignatures = thenFunction ? getSignaturesOfType(thenFunction, SignatureKind.Call) : emptyArray; let thenSignatures = thenFunction ? getSignaturesOfType(thenFunction, SignatureKind.Call) : emptyArray;
if (thenSignatures.length === 0) { if (thenSignatures.length === 0) {
return undefined; return undefined;
} }
let onfulfilledParameterType = getUnionType(map(thenSignatures, getTypeOfFirstParameterOfSignature)); let onfulfilledParameterType = getUnionType(map(thenSignatures, getTypeOfFirstParameterOfSignature));
if (onfulfilledParameterType.flags & TypeFlags.Any) { if (onfulfilledParameterType.flags & TypeFlags.Any) {
return undefined; return undefined;
} }
let onfulfilledParameterSignatures = getSignaturesOfType(onfulfilledParameterType, SignatureKind.Call); let onfulfilledParameterSignatures = getSignaturesOfType(onfulfilledParameterType, SignatureKind.Call);
if (onfulfilledParameterSignatures.length === 0) { if (onfulfilledParameterSignatures.length === 0) {
return undefined; return undefined;
} }
let valueParameterType = getUnionType(map(onfulfilledParameterSignatures, getTypeOfFirstParameterOfSignature)); let valueParameterType = getUnionType(map(onfulfilledParameterSignatures, getTypeOfFirstParameterOfSignature));
return valueParameterType; return valueParameterType;
} }
function getTypeOfFirstParameterOfSignature(signature: Signature) { function getTypeOfFirstParameterOfSignature(signature: Signature) {
return getTypeAtPosition(signature, 0); return getTypeAtPosition(signature, 0);
} }
/** /**
* Gets the "awaited type" of a type. * Gets the "awaited type" of a type.
* @param type The type to await. * @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 * Promise-like type; otherwise, it is the type of the expression. This is used to reflect
* The runtime behavior of the `await` keyword. * The runtime behavior of the `await` keyword.
*/ */
function getAwaitedType(type: Type) { function getAwaitedType(type: Type) {
return checkAwaitedType(type, /*location*/ undefined, /*message*/ undefined); return checkAwaitedType(type, /*location*/ undefined, /*message*/ undefined);
} }
function checkAwaitedType(type: Type, location?: Node, message?: DiagnosticMessage) { function checkAwaitedType(type: Type, location?: Node, message?: DiagnosticMessage) {
return checkAwaitedTypeWorker(type); return checkAwaitedTypeWorker(type);
function checkAwaitedTypeWorker(type: Type): Type { function checkAwaitedTypeWorker(type: Type): Type {
if (type.flags & TypeFlags.Union) { if (type.flags & TypeFlags.Union) {
let types: Type[] = []; let types: Type[] = [];
for (let constituentType of (<UnionType>type).types) { for (let constituentType of (<UnionType>type).types) {
types.push(checkAwaitedTypeWorker(constituentType)); types.push(checkAwaitedTypeWorker(constituentType));
} }
return getUnionType(types); return getUnionType(types);
} }
else { else {
let promisedType = getPromisedType(type); let promisedType = getPromisedType(type);
if (promisedType === undefined) { if (promisedType === undefined) {
// The type was not a PromiseLike, so it could not be unwrapped any further. // 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 // safe to return the type; otherwise, an error will have been reported in
// the call to checkNonThenableType and we will return unknownType. // 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 // 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 // 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 // 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 // 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 <any>. // be treated as a promise, they can cast to <any>.
return checkNonThenableType(type, location, message); return checkNonThenableType(type, location, message);
@ -10779,70 +10791,70 @@ namespace ts {
else { else {
if (type.id === promisedType.id || awaitedTypeStack.indexOf(promisedType.id) >= 0) { 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 // 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 // the same promise type, or a mutually recursive promise. Return the
// unknown type as we cannot guess the shape. If this were the actual // unknown type as we cannot guess the shape. If this were the actual
// case in the JavaScript, this Promise would never resolve. // 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: // be:
// //
// interface BadPromise { // interface BadPromise {
// then( // then(
// onfulfilled: (value: BadPromise) => any, // onfulfilled: (value: BadPromise) => any,
// onrejected: (error: any) => any): BadPromise; // onrejected: (error: any) => any): BadPromise;
// } // }
// //
// The above interface will pass the PromiseLike check, and return a // The above interface will pass the PromiseLike check, and return a
// promised type of `BadPromise`. Since this is a self reference, we // promised type of `BadPromise`. Since this is a self reference, we
// don't want to keep recursing ad infinitum. // 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: // promise type might be:
// //
// interface BadPromiseA { // interface BadPromiseA {
// then( // then(
// onfulfilled: (value: BadPromiseB) => any, // onfulfilled: (value: BadPromiseB) => any,
// onrejected: (error: any) => any): BadPromiseB; // onrejected: (error: any) => any): BadPromiseB;
// } // }
// //
// interface BadPromiseB { // interface BadPromiseB {
// then( // then(
// onfulfilled: (value: BadPromiseA) => any, // onfulfilled: (value: BadPromiseA) => any,
// onrejected: (error: any) => any): BadPromiseA; // onrejected: (error: any) => any): BadPromiseA;
// } // }
// //
if (location) { if (location) {
error( error(
location, location,
Diagnostics._0_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method, Diagnostics._0_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method,
symbolToString(type.symbol)); symbolToString(type.symbol));
} }
return unknownType; return unknownType;
} }
// Keep track of the type we're about to unwrap to avoid bad recursive promise types. // Keep track of the type we're about to unwrap to avoid bad recursive promise types.
// See the comments above for more information. // See the comments above for more information.
awaitedTypeStack.push(type.id); awaitedTypeStack.push(type.id);
let awaitedType = checkAwaitedTypeWorker(promisedType); let awaitedType = checkAwaitedTypeWorker(promisedType);
awaitedTypeStack.pop(); 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. * Promise implementation.
* @param node The signature to check * @param node The signature to check
* @param returnType The return type for the function * @param returnType The return type for the function
* @remarks * @remarks
* This checks that an async function has a valid Promise-compatible return type, * 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 * 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 * 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 * 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. * callable `then` signature.
*/ */
function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration): Type { 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 // 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 // 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`. // side or "constructor" of the promise type) is compatible `PromiseConstructorLike`.
// //
// An example might be (from lib.es6.d.ts): // An example might be (from lib.es6.d.ts):
@ -10863,11 +10875,11 @@ namespace ts {
// interface Promise<T> { ... } // interface Promise<T> { ... }
// interface PromiseConstructor { // interface PromiseConstructor {
// new <T>(...): Promise<T>; // new <T>(...): Promise<T>;
// } // }
// declare var Promise: PromiseConstructor; // declare var Promise: PromiseConstructor;
// //
// When an async function declares a return type annotation of `Promise<T>`, we // When an async function declares a return type annotation of `Promise<T>`, we
// need to get the type of the `Promise` variable declaration above, which would // need to get the type of the `Promise` variable declaration above, which would
// be `PromiseConstructor`. // be `PromiseConstructor`.
// //
// The same case applies to a class: // 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 // 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 <T>(...): Promise<T> }`. // side of the `Promise` class, which would be `{ new <T>(...): Promise<T> }`.
let promiseType = getTypeFromTypeNode(node.type); let promiseType = getTypeFromTypeNode(node.type);
if (promiseType === unknownType && compilerOptions.isolatedModules) { 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; // type as a value. As such, we will just return unknownType;
return unknownType; return unknownType;
} }
let promiseConstructor = getMergedSymbol(promiseType.symbol); let promiseConstructor = getMergedSymbol(promiseType.symbol);
if (!promiseConstructor || !symbolIsValue(promiseConstructor)) { if (!promiseConstructor || !symbolIsValue(promiseConstructor)) {
error(node, Diagnostics.Type_0_is_not_a_valid_async_function_return_type, typeToString(promiseType)); error(node, Diagnostics.Type_0_is_not_a_valid_async_function_return_type, typeToString(promiseType));
return unknownType return unknownType
} }
// Validate the promise constructor type. // Validate the promise constructor type.
let promiseConstructorType = getTypeOfSymbol(promiseConstructor); let promiseConstructorType = getTypeOfSymbol(promiseConstructor);
if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, node, Diagnostics.Type_0_is_not_a_valid_async_function_return_type)) { 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. // 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); return checkAwaitedType(promiseType, node, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
} }
/** Check a decorator */ /** Check a decorator */
function checkDecorator(node: Decorator): void { function checkDecorator(node: Decorator): void {
let signature = getResolvedSignature(node); let signature = getResolvedSignature(node);
@ -10963,11 +10975,11 @@ namespace ts {
headMessage, headMessage,
errorInfo); errorInfo);
} }
/** Checks a type reference node as an expression. */ /** Checks a type reference node as an expression. */
function checkTypeNodeAsExpression(node: TypeNode) { function checkTypeNodeAsExpression(node: TypeNode) {
// When we are emitting type metadata for decorators, we need to try to check the type // 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. // serialize the type metadata.
if (node && node.kind === SyntaxKind.TypeReference) { if (node && node.kind === SyntaxKind.TypeReference) {
let type = getTypeFromTypeNode(node); 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. * an expression if it is a type reference to a type with a value declaration.
*/ */
function checkTypeAnnotationAsExpression(node: AccessorDeclaration | PropertyDeclaration | ParameterDeclaration | MethodDeclaration) { function checkTypeAnnotationAsExpression(node: AccessorDeclaration | PropertyDeclaration | ParameterDeclaration | MethodDeclaration) {
@ -11004,7 +11016,7 @@ namespace ts {
break; break;
} }
} }
/** Checks the type annotation of the parameters of a function/method or the constructor of a class as expressions */ /** Checks the type annotation of the parameters of a function/method or the constructor of a class as expressions */
function checkParameterTypeAnnotationsAsExpressions(node: FunctionLikeDeclaration) { function checkParameterTypeAnnotationsAsExpressions(node: FunctionLikeDeclaration) {
// ensure all type annotations with a value declaration are checked as an expression // ensure all type annotations with a value declaration are checked as an expression
@ -11078,9 +11090,9 @@ namespace ts {
if (!compilerOptions.experimentalAsyncFunctions) { 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); 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; emitAwaiter = true;
} }
// Do not use hasDynamicName here, because that returns false for well known symbols. // Do not use hasDynamicName here, because that returns false for well known symbols.
// We want to perform checkComputedPropertyName for all computed properties, including // We want to perform checkComputedPropertyName for all computed properties, including
@ -11120,7 +11132,7 @@ namespace ts {
if (isAsync) { if (isAsync) {
promisedType = checkAsyncFunctionReturnType(node); promisedType = checkAsyncFunctionReturnType(node);
} }
checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, isAsync ? promisedType : returnType); 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 // Check that a parameter initializer contains no references to parameters declared to the right of itself
function checkParameterInitializer(node: VariableLikeDeclaration): void { function checkParameterInitializer(node: VariableLikeDeclaration): void {
if (getRootDeclaration(node).kind !== SyntaxKind.Parameter) { if (getRootDeclaration(node).kind !== SyntaxKind.Parameter) {
@ -11653,7 +11665,7 @@ namespace ts {
return elementType || anyType; return elementType || anyType;
} }
/** /**
* We want to treat type as an iterable, and get the type it is an iterable of. The iterable * 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): * 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, // 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* // 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. // 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 // derived class inherits base without override/redeclaration
let derivedClassDecl = getDeclarationOfKind(type.symbol, SyntaxKind.ClassDeclaration); let derivedClassDecl = getDeclarationOfKind(type.symbol, SyntaxKind.ClassDeclaration);
// It is an error to inherit an abstract member without implementing it or being declared abstract. // 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), // If there is no declaration for the derived class (as in the case of class expressions),
// then the class cannot be declared abstract. // then the class cannot be declared abstract.
if ( baseDeclarationFlags & NodeFlags.Abstract && (!derivedClassDecl || !(derivedClassDecl.flags & NodeFlags.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, error(derivedClassDecl, Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2,
typeToString(type), symbolToString(baseProperty), typeToString(baseType)); typeToString(type), symbolToString(baseProperty), typeToString(baseType));
} }
} }
else { else {
// derived overrides base. // derived overrides base.
let derivedDeclarationFlags = getDeclarationFlagsFromSymbol(derived); let derivedDeclarationFlags = getDeclarationFlagsFromSymbol(derived);
if ((baseDeclarationFlags & NodeFlags.Private) || (derivedDeclarationFlags & NodeFlags.Private)) { 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. // we need to track this to ensure the correct emit.
let mergedClass = getDeclarationOfKind(symbol, SyntaxKind.ClassDeclaration); let mergedClass = getDeclarationOfKind(symbol, SyntaxKind.ClassDeclaration);
if (mergedClass && if (mergedClass &&
@ -13298,7 +13310,7 @@ namespace ts {
forEach(potentialThisCollisions, checkIfThisIsCapturedInEnclosingScope); forEach(potentialThisCollisions, checkIfThisIsCapturedInEnclosingScope);
potentialThisCollisions.length = 0; potentialThisCollisions.length = 0;
} }
if (emitExtends) { if (emitExtends) {
links.flags |= NodeCheckFlags.EmitExtends; links.flags |= NodeCheckFlags.EmitExtends;
} }
@ -13310,11 +13322,11 @@ namespace ts {
if (emitParam) { if (emitParam) {
links.flags |= NodeCheckFlags.EmitParam; links.flags |= NodeCheckFlags.EmitParam;
} }
if (emitAwaiter) { if (emitAwaiter) {
links.flags |= NodeCheckFlags.EmitAwaiter; links.flags |= NodeCheckFlags.EmitAwaiter;
} }
if (emitGenerator || (emitAwaiter && languageVersion < ScriptTarget.ES6)) { if (emitGenerator || (emitAwaiter && languageVersion < ScriptTarget.ES6)) {
links.flags |= NodeCheckFlags.EmitGenerator; 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". * whether the element is declared as "static".
*/ */
function getParentTypeOfClassElement(node: ClassElement) { function getParentTypeOfClassElement(node: ClassElement) {
@ -13722,7 +13734,7 @@ namespace ts {
? getTypeOfSymbol(classSymbol) ? getTypeOfSymbol(classSymbol)
: getDeclaredTypeOfSymbol(classSymbol); : getDeclaredTypeOfSymbol(classSymbol);
} }
// Return the list of properties of the given type, augmented with properties from Function // Return the list of properties of the given type, augmented with properties from Function
// if the type has call or construct signatures // if the type has call or construct signatures
function getAugmentedPropertiesOfType(type: Type): Symbol[] { 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 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 FunctionLikeDeclaration is "Function".
// * The serialized type of any other node is "void 0". // * The serialized type of any other node is "void 0".
// //
// For rules on serializing type annotations, see `serializeTypeNode`. // For rules on serializing type annotations, see `serializeTypeNode`.
switch (node.kind) { switch (node.kind) {
case SyntaxKind.ClassDeclaration: return "Function"; case SyntaxKind.ClassDeclaration: return "Function";
@ -14075,14 +14087,14 @@ namespace ts {
} }
return "void 0"; 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. */ /** 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[])[] { function serializeParameterTypesOfNode(node: Node): (string | string[])[] {
// serialization of parameter types uses the following rules: // 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 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. // * 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`. // For the rules on serializing the type of each parameter declaration, see `serializeTypeOfDeclaration`.
if (node) { if (node) {
var valueDeclaration: FunctionLikeDeclaration; var valueDeclaration: FunctionLikeDeclaration;
@ -14299,21 +14311,21 @@ namespace ts {
anyArrayType = createArrayType(anyType); anyArrayType = createArrayType(anyType);
} }
function createInstantiatedPromiseLikeType(): ObjectType { function createInstantiatedPromiseLikeType(): ObjectType {
let promiseLikeType = getGlobalPromiseLikeType(); let promiseLikeType = getGlobalPromiseLikeType();
if (promiseLikeType !== emptyObjectType) { if (promiseLikeType !== emptyObjectType) {
return createTypeReference(<GenericType>promiseLikeType, [anyType]); return createTypeReference(<GenericType>promiseLikeType, [anyType]);
} }
return emptyObjectType; return emptyObjectType;
} }
function createThenableType() { function createThenableType() {
// build the thenable type that is used to verify against a non-promise "thenable" operand to `await`. // 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"); let thenPropertySymbol = createSymbol(SymbolFlags.Transient | SymbolFlags.Property, "then");
getSymbolLinks(thenPropertySymbol).type = globalFunctionType; getSymbolLinks(thenPropertySymbol).type = globalFunctionType;
let thenableType = <ResolvedType>createObjectType(TypeFlags.Anonymous); let thenableType = <ResolvedType>createObjectType(TypeFlags.Anonymous);
thenableType.properties = [thenPropertySymbol]; thenableType.properties = [thenPropertySymbol];
thenableType.members = createSymbolTable(thenableType.properties); thenableType.members = createSymbolTable(thenableType.properties);

View file

@ -401,7 +401,7 @@ namespace ts {
} }
function emitTypePredicate(type: TypePredicateNode) { function emitTypePredicate(type: TypePredicateNode) {
writeEntityName(type.parameterName); writeTextOfNode(currentSourceFile, type.parameterName);
write(" is "); write(" is ");
emitType(type.type); emitType(type.type);
} }

View file

@ -31,6 +31,15 @@ export function fooWithSingleOverload(a: any) {
export function fooWithTypePredicate(a: any): a is number { export function fooWithTypePredicate(a: any): a is number {
return true; return true;
} }
export function fooWithTypePredicateAndMulitpleParams(a: any, b: any, c: any): a is number {
return true;
}
export function fooWithTypeTypePredicateAndGeneric<T>(a: any): a is T {
return true;
}
export function fooWithTypeTypePredicateAndRestParam(a: any, ...rest): a is number {
return true;
}
/** This comment should appear for nonExportedFoo*/ /** This comment should appear for nonExportedFoo*/
function nonExportedFoo() { function nonExportedFoo() {

View file

@ -0,0 +1,20 @@
/// <reference path="fourslash.ts" />
//// function f1(a: any): a is number {}
//// function f2<T>(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<T>(a: any): a is T");
goTo.marker("3");
verify.signatureHelpCountIs(1);
verify.currentSignatureHelpIs("f3(a: any, ...b: any[]): a is number");