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.
// 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<TupleType> = {};
let unionTypes: Map<UnionType> = {};
let intersectionTypes: Map<IntersectionType> = {};
@ -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 && (<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(<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
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(<GenericType>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(<ClassElement>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 = <ClassElement>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<T>` 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<T>` type where `T` is the promisedType argument
let globalPromiseType = getGlobalPromiseType();
@ -8806,7 +8818,7 @@ namespace ts {
promisedType = getAwaitedType(promisedType);
return createTypeReference(<GenericType>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(<Expression>func.body, contextualMapper);
type = checkExpressionCached(<Expression>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<T> 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<T> 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<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)) {
@ -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(<Expression>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) && (<GenericType>promise).target === tryGetGlobalPromiseType()) {
return (<GenericType>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 (<UnionType>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 <any>.
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<T> { ... }
// interface PromiseConstructor {
// new <T>(...): Promise<T>;
// }
// }
// declare var Promise: PromiseConstructor;
//
// 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
// 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
// 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 <T>(...): Promise<T> }`.
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(<GenericType>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 = <ResolvedType>createObjectType(TypeFlags.Anonymous);
thenableType.properties = [thenPropertySymbol];
thenableType.members = createSymbolTable(thenableType.properties);

View file

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

View file

@ -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<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*/
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");