diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 340bf47820940..90f819305d36f 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -96,7 +96,9 @@ namespace ts { let symbolCount = 0; let Symbol = objectAllocator.getSymbolConstructor(); - let classifiableNames: Map = {}; + + let isJavaScriptFile = isJavaScript(file.fileName); + let classifiableNames: Map = {}; if (!file.locals) { bind(file); @@ -166,6 +168,15 @@ namespace ts { case SyntaxKind.FunctionDeclaration: case SyntaxKind.ClassDeclaration: return node.flags & NodeFlags.Default ? "default" : undefined; + case SyntaxKind.JSDocFunctionType: + return isJSDocConstructSignature(node) ? "__new" : "__call"; + case SyntaxKind.Parameter: + // Parameters with names are handled at the top of this function. Parameters + // without names can only come from JSDocFunctionTypes. + Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType); + let functionType = node.parent; + let index = indexOf(functionType.parameters, node); + return "p" + index; } } @@ -329,6 +340,10 @@ namespace ts { blockScopeContainer.locals = undefined; } + if (isJavaScriptFile && node.jsDocComment) { + bind(node.jsDocComment); + } + forEachChild(node, bind); container = saveContainer; @@ -342,8 +357,9 @@ namespace ts { case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: - case SyntaxKind.TypeLiteral: case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.TypeLiteral: + case SyntaxKind.JSDocRecordType: return ContainerFlags.IsContainer; case SyntaxKind.CallSignature: @@ -429,6 +445,7 @@ namespace ts { case SyntaxKind.TypeLiteral: case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.JSDocRecordType: // Interface/Object-types always have their children added to the 'members' of // their container. They are only accessible through an instance of their // container, and are never in scope otherwise (even inside the body of the @@ -449,6 +466,7 @@ namespace ts { case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: + case SyntaxKind.JSDocFunctionType: case SyntaxKind.TypeAliasDeclaration: // All the children of these container types are never visible through another // symbol (i.e. through another symbol's 'exports' or 'members'). Instead, @@ -538,7 +556,7 @@ namespace ts { } } - function bindFunctionOrConstructorType(node: SignatureDeclaration) { + function bindFunctionOrConstructorTypeORJSDocFunctionType(node: SignatureDeclaration): void { // For a given function symbol "<...>(...) => T" we want to generate a symbol identical // to the one we would get for: { <...>(...): T } // @@ -763,7 +781,7 @@ namespace ts { return "__" + indexOf((node.parent).parameters, node); } - function bind(node: Node) { + function bind(node: Node): void { node.parent = parent; let savedInStrictMode = inStrictMode; @@ -861,6 +879,7 @@ namespace ts { return bindVariableDeclarationOrBindingElement(node); case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: + case SyntaxKind.JSDocRecordMember: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | ((node).questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes); case SyntaxKind.PropertyAssignment: case SyntaxKind.ShorthandPropertyAssignment: @@ -890,8 +909,10 @@ namespace ts { return bindPropertyOrMethodOrAccessor(node, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: - return bindFunctionOrConstructorType(node); + case SyntaxKind.JSDocFunctionType: + return bindFunctionOrConstructorTypeORJSDocFunctionType(node); case SyntaxKind.TypeLiteral: + case SyntaxKind.JSDocRecordType: return bindAnonymousDeclaration(node, SymbolFlags.TypeLiteral, "__type"); case SyntaxKind.ObjectLiteralExpression: return bindObjectLiteralExpression(node); @@ -1062,4 +1083,4 @@ namespace ts { : declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); } } -} +} diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9babfdec33925..742f462750cd7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2360,8 +2360,54 @@ namespace ts { return type; } + function getTypeForVariableLikeDeclarationFromJSDocComment(declaration: VariableLikeDeclaration) { + let jsDocType = getJSDocTypeForVariableLikeDeclarationFromJSDocComment(declaration); + if (jsDocType) { + return getTypeFromTypeNode(jsDocType); + } + } + + function getJSDocTypeForVariableLikeDeclarationFromJSDocComment(declaration: VariableLikeDeclaration): JSDocType { + // First, see if this node has an @type annotation on it directly. + let typeTag = getJSDocTypeTag(declaration); + if (typeTag) { + return typeTag.typeExpression.type; + } + + if (declaration.kind === SyntaxKind.VariableDeclaration && + declaration.parent.kind === SyntaxKind.VariableDeclarationList && + declaration.parent.parent.kind === SyntaxKind.VariableStatement) { + + // @type annotation might have been on the variable statement, try that instead. + let typeTag = getJSDocTypeTag(declaration.parent.parent); + if (typeTag) { + return typeTag.typeExpression.type; + } + } + else if (declaration.kind === SyntaxKind.Parameter) { + // If it's a parameter, see if the parent has a jsdoc comment with an @param + // annotation. + let paramTag = getCorrespondingJSDocParameterTag(declaration); + if (paramTag && paramTag.typeExpression) { + return paramTag.typeExpression.type; + } + } + + return undefined; + } + // Return the inferred type for a variable, parameter, or property declaration function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration): Type { + if (declaration.parserContextFlags & ParserContextFlags.JavaScriptFile) { + // If this is a variable in a JavaScript file, then use the JSDoc type (if it has + // one as it's type), otherwise fallback to the below standard TS codepaths to + // try to figure it out. + let type = getTypeForVariableLikeDeclarationFromJSDocComment(declaration); + if (type && type !== unknownType) { + return type; + } + } + // A variable declared in a for..in statement is always of type any if (declaration.parent.parent.kind === SyntaxKind.ForInStatement) { return anyType; @@ -2378,7 +2424,7 @@ namespace ts { if (isBindingPattern(declaration.parent)) { return getTypeForBindingElement(declaration); } - + // Use type from type annotation if one is present if (declaration.type) { return getTypeFromTypeNode(declaration.type); @@ -3513,9 +3559,24 @@ namespace ts { return getIndexTypeOfStructuredType(getApparentType(type), kind); } + function getTypeParametersFromSignatureDeclaration(declaration: SignatureDeclaration): TypeParameter[] { + if (declaration.parserContextFlags & ParserContextFlags.JavaScriptFile) { + let templateTag = getJSDocTemplateTag(declaration); + if (templateTag) { + return getTypeParametersFromTypeParameterDeclarations(templateTag.typeParameters); + } + } + + if (declaration.typeParameters) { + return getTypeParametersFromTypeParameterDeclarations(declaration.typeParameters); + } + + return undefined; + } + // Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual // type checking functions). - function getTypeParametersFromDeclaration(typeParameterDeclarations: TypeParameterDeclaration[]): TypeParameter[] { + function getTypeParametersFromTypeParameterDeclarations(typeParameterDeclarations: TypeParameterDeclaration[]): TypeParameter[] { let result: TypeParameter[] = []; forEach(typeParameterDeclarations, node => { let tp = getDeclaredTypeOfTypeParameter(node.symbol); @@ -3536,12 +3597,37 @@ namespace ts { return result; } - function isOptionalParameter(node: ParameterDeclaration) { + function isRestOrOptionalParameter(node: ParameterDeclaration, skipSignatureCheck?: boolean) { + return isRestParameter(node) || isOptionalParameter(node, skipSignatureCheck); + } + + function isOptionalParameter(node: ParameterDeclaration, skipSignatureCheck?: boolean) { + if (node.parserContextFlags & ParserContextFlags.JavaScriptFile) { + if (node.type && node.type.kind === SyntaxKind.JSDocOptionalType) { + return true; + } + + let paramTag = getCorrespondingJSDocParameterTag(node); + if (paramTag) { + if (paramTag.isBracketed) { + return true; + } + + if (paramTag.typeExpression) { + return paramTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType; + } + } + } + if (hasQuestionToken(node)) { return true; } if (node.initializer) { + if (skipSignatureCheck) { + return true; + } + let signatureDeclaration = node.parent; let signature = getSignatureFromDeclaration(signatureDeclaration); let parameterIndex = signatureDeclaration.parameters.indexOf(node); @@ -3555,20 +3641,27 @@ namespace ts { function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature { let links = getNodeLinks(declaration); if (!links.resolvedSignature) { - let classType = declaration.kind === SyntaxKind.Constructor ? getDeclaredTypeOfClassOrInterface((declaration.parent).symbol) : undefined; - let typeParameters = classType ? classType.localTypeParameters : - declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) : undefined; let parameters: Symbol[] = []; let hasStringLiterals = false; let minArgumentCount = -1; - for (let i = 0, n = declaration.parameters.length; i < n; i++) { + let returnType: Type; + let typePredicate: TypePredicate; + + let classType = declaration.kind === SyntaxKind.Constructor ? getDeclaredTypeOfClassOrInterface((declaration.parent).symbol) : undefined; + let typeParameters = classType ? classType.localTypeParameters : getTypeParametersFromSignatureDeclaration(declaration); + let isJSConstructSignature = isJSDocConstructSignature(declaration); + + // If this is a JSDoc construct signature, then skip the first parameter in the + // parameter list. The first parameter represents the return type of the construct + // signature. + for (let i = isJSConstructSignature ? 1 : 0, n = declaration.parameters.length; i < n; i++) { let param = declaration.parameters[i]; parameters.push(param.symbol); if (param.type && param.type.kind === SyntaxKind.StringLiteral) { hasStringLiterals = true; } - if (param.initializer || param.questionToken || param.dotDotDotToken) { + if (isRestOrOptionalParameter(param, /*skipSignatureCheck*/ true)) { if (minArgumentCount < 0) { minArgumentCount = i; } @@ -3583,9 +3676,11 @@ namespace ts { minArgumentCount = declaration.parameters.length; } - let returnType: Type; - let typePredicate: TypePredicate; - if (classType) { + if (isJSConstructSignature) { + minArgumentCount--; + returnType = getTypeFromTypeNode(declaration.parameters[0].type); + } + else if (classType) { returnType = classType; } else if (declaration.type) { @@ -3637,6 +3732,7 @@ namespace ts { case SyntaxKind.SetAccessor: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: + case SyntaxKind.JSDocFunctionType: // Don't include signature if node is the implementation of an overloaded function. A node is considered // an implementation node if it has a body and the previous node is of the same kind and immediately // precedes the implementation node (i.e. has the same parent and ends where the implementation starts). @@ -3818,7 +3914,10 @@ namespace ts { return type; } - function isTypeParameterReferenceIllegalInConstraint(typeReferenceNode: TypeReferenceNode | ExpressionWithTypeArguments, typeParameterSymbol: Symbol): boolean { + function isTypeParameterReferenceIllegalInConstraint( + typeReferenceNode: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, + typeParameterSymbol: Symbol): boolean { + let links = getNodeLinks(typeReferenceNode); if (links.isIllegalTypeReferenceInConstraint !== undefined) { return links.isIllegalTypeReferenceInConstraint; @@ -3827,7 +3926,7 @@ namespace ts { // bubble up to the declaration let currentNode: Node = typeReferenceNode; // forEach === exists - while (!forEach(typeParameterSymbol.declarations, d => d.parent === currentNode.parent)) { + while (!forEach(typeParameterSymbol.declarations, d => getTypeParameterOwner(d) === currentNode.parent)) { currentNode = currentNode.parent; } // if last step was made from the type parameter this means that path has started somewhere in constraint which is illegal @@ -3868,7 +3967,7 @@ namespace ts { } // Get type from reference to class or interface - function getTypeFromClassOrInterfaceReference(node: TypeReferenceNode | ExpressionWithTypeArguments, symbol: Symbol): Type { + function getTypeFromClassOrInterfaceReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol): Type { let type = getDeclaredTypeOfSymbol(symbol); let typeParameters = (type).localTypeParameters; if (typeParameters) { @@ -3892,7 +3991,7 @@ namespace ts { // Get type from reference to type alias. When a type alias is generic, the declared type of the type alias may include // references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the // declared type. Instantiations are cached using the type identities of the type arguments as the key. - function getTypeFromTypeAliasReference(node: TypeReferenceNode | ExpressionWithTypeArguments, symbol: Symbol): Type { + function getTypeFromTypeAliasReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol): Type { let type = getDeclaredTypeOfSymbol(symbol); let links = getSymbolLinks(symbol); let typeParameters = links.typeParameters; @@ -3913,7 +4012,7 @@ namespace ts { } // Get type from reference to named type that cannot be generic (enum or type parameter) - function getTypeFromNonGenericTypeReference(node: TypeReferenceNode | ExpressionWithTypeArguments, symbol: Symbol): Type { + function getTypeFromNonGenericTypeReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol): Type { if (symbol.flags & SymbolFlags.TypeParameter && isTypeParameterReferenceIllegalInConstraint(node, symbol)) { // TypeScript 1.0 spec (April 2014): 3.4.1 // Type parameters declared in a particular type parameter list @@ -3928,23 +4027,79 @@ namespace ts { return getDeclaredTypeOfSymbol(symbol); } - function getTypeFromTypeReference(node: TypeReferenceNode | ExpressionWithTypeArguments): Type { + function getTypeReferenceName(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference): LeftHandSideExpression | EntityName { + switch (node.kind) { + case SyntaxKind.TypeReference: + return (node).typeName; + case SyntaxKind.JSDocTypeReference: + return (node).name; + case SyntaxKind.ExpressionWithTypeArguments: + // We only support expressions that are simple qualified names. For other + // expressions this produces undefined. + if (isSupportedExpressionWithTypeArguments(node)) { + return (node).expression; + } + + // fall through; + } + + return undefined; + } + + function resolveTypeReferenceName( + node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, + typeReferenceName: LeftHandSideExpression | EntityName) { + + if (!typeReferenceName) { + return unknownSymbol; + } + + let symbol = resolveEntityName(typeReferenceName, SymbolFlags.Type); + if (!symbol && node.kind === SyntaxKind.JSDocTypeReference) { + // If the reference didn't resolve to a type, try seeing if results to a + // value. If it does, get the type of that value. + symbol = resolveEntityName(typeReferenceName, SymbolFlags.Value); + } + + return symbol || unknownSymbol; + } + + function getTypeReferenceType(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol) { + if (symbol === unknownSymbol) { + return unknownType; + } + + if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { + return getTypeFromClassOrInterfaceReference(node, symbol); + } + + if (symbol.flags & SymbolFlags.TypeAlias) { + return getTypeFromTypeAliasReference(node, symbol); + } + + if (symbol.flags & SymbolFlags.Value && node.kind === SyntaxKind.JSDocTypeReference) { + // A JSDocTypeReference may have resolved to a value (as opposed to a type). In + // that case, the type of this reference is just the type of the value we resolved + // to. + return getTypeOfSymbol(symbol); + } + + return getTypeFromNonGenericTypeReference(node, symbol); + } + + function getTypeFromTypeReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference): Type { let links = getNodeLinks(node); if (!links.resolvedType) { - // We only support expressions that are simple qualified names. For other expressions this produces undefined. - let typeNameOrExpression = node.kind === SyntaxKind.TypeReference ? (node).typeName : - isSupportedExpressionWithTypeArguments(node) ? (node).expression : - undefined; - let symbol = typeNameOrExpression && resolveEntityName(typeNameOrExpression, SymbolFlags.Type) || unknownSymbol; - let type = symbol === unknownSymbol ? unknownType : - symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface) ? getTypeFromClassOrInterfaceReference(node, symbol) : - symbol.flags & SymbolFlags.TypeAlias ? getTypeFromTypeAliasReference(node, symbol) : - getTypeFromNonGenericTypeReference(node, symbol); + let typeReferenceName = getTypeReferenceName(node); + let symbol = resolveTypeReferenceName(node, typeReferenceName); + let type = getTypeReferenceType(node, symbol); + // Cache both the resolved symbol and the resolved type. The resolved symbol is needed in when we check the // type reference in checkTypeReferenceOrExpressionWithTypeArguments. links.resolvedSymbol = symbol; links.resolvedType = type; } + return links.resolvedType; } @@ -4288,6 +4443,40 @@ namespace ts { return links.resolvedType; } + function getTypeFromJSDocFunctionType(node: JSDocFunctionType): Type { + Debug.assert(!!node.symbol); + return createObjectType(TypeFlags.Anonymous, node.symbol); + } + + function getTypeFromJSDocRecordType(node: JSDocRecordType): Type { + return createObjectType(TypeFlags.Anonymous, node.symbol); + } + + function getTypeFromJSDocVariadicType(node: JSDocVariadicType): Type { + let type = getTypeFromTypeNode(node.type); + if (type) { + return createArrayType(type); + } + } + + function getTypeForJSDocTypeReference(node: JSDocTypeReference): Type { + return getTypeFromTypeReference(node); + } + + function getTypeFromJSDocArrayType(node: JSDocArrayType): Type { + return createArrayType(getTypeFromTypeNode(node.elementType)); + } + + function getTypeFromJSDocUnionType(node: JSDocUnionType): Type { + let types = map(node.types, getTypeFromTypeNode); + return getUnionType(types, /*noSubtypeReduction*/ true); + } + + function getTypeFromJSDocTupleType(node: JSDocTupleType): Type { + let types = map(node.types, getTypeFromTypeNode); + return createTupleType(types); + } + function getTypeFromTypeNode(node: TypeNode): Type { switch (node.kind) { case SyntaxKind.AnyKeyword: @@ -4332,9 +4521,37 @@ namespace ts { case SyntaxKind.QualifiedName: let symbol = getSymbolAtLocation(node); return symbol && getDeclaredTypeOfSymbol(symbol); - default: + case SyntaxKind.JSDocAllType: + return anyType; + case SyntaxKind.JSDocUnknownType: return unknownType; + case SyntaxKind.JSDocArrayType: + return getTypeFromJSDocArrayType(node); + case SyntaxKind.JSDocTupleType: + return getTypeFromJSDocTupleType(node); + case SyntaxKind.JSDocUnionType: + return getTypeFromJSDocUnionType(node); + case SyntaxKind.JSDocNullableType: + return getTypeFromTypeNode((node).type); + case SyntaxKind.JSDocNonNullableType: + return getTypeFromTypeNode((node).type); + case SyntaxKind.JSDocTypeReference: + return getTypeForJSDocTypeReference(node); + case SyntaxKind.JSDocOptionalType: + return getTypeFromTypeNode((node).type); + case SyntaxKind.JSDocFunctionType: + return getTypeFromJSDocFunctionType(node); + case SyntaxKind.JSDocVariadicType: + return getTypeFromJSDocVariadicType(node); + case SyntaxKind.JSDocConstructorType: + return getTypeFromTypeNode((node).type); + case SyntaxKind.JSDocRecordType: + return getTypeFromJSDocRecordType(node); + case SyntaxKind.JSDocThisType: + return getTypeFromTypeNode((node).type); } + + return unknownType; } function instantiateList(items: T[], mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T[] { @@ -6467,9 +6684,27 @@ namespace ts { let symbol = getSymbolOfNode(container.parent); return container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : getDeclaredTypeOfSymbol(symbol); } + + if (container.parserContextFlags & ParserContextFlags.JavaScriptFile) { + let type = getTypeForThisExpressionFromJSDoc(container); + if (type && type !== unknownType) { + return type; + } + } + return anyType; } + function getTypeForThisExpressionFromJSDoc(node: Node) { + let typeTag = getJSDocTypeTag(node); + if (typeTag && typeTag.typeExpression.type.kind === SyntaxKind.JSDocFunctionType) { + let jsDocFunctionType = typeTag.typeExpression.type; + if (jsDocFunctionType.parameters.length > 0 && jsDocFunctionType.parameters[0].type.kind === SyntaxKind.JSDocThisType) { + return getTypeFromTypeNode(jsDocFunctionType.parameters[0].type); + } + } + } + function isInConstructorArgumentInitializer(node: Node, constructorDecl: Node): boolean { for (let n = node; n && n !== constructorDecl; n = n.parent) { if (n.kind === SyntaxKind.Parameter) { @@ -9068,7 +9303,8 @@ namespace ts { if (declaration && declaration.kind !== SyntaxKind.Constructor && declaration.kind !== SyntaxKind.ConstructSignature && - declaration.kind !== SyntaxKind.ConstructorType) { + declaration.kind !== SyntaxKind.ConstructorType && + !isJSDocConstructSignature(declaration)) { // When resolved signature is a call signature (and not a construct signature) the result type is any if (compilerOptions.noImplicitAny) { @@ -9155,6 +9391,13 @@ namespace ts { } } + function getReturnTypeFromJSDocComment(func: FunctionLikeDeclaration): Type { + let returnTag = getJSDocReturnTag(func); + if (returnTag) { + return getTypeFromTypeNode(returnTag.typeExpression.type); + } + } + function createPromiseType(promisedType: Type): Type { // creates a `Promise` type where `T` is the promisedType argument let globalPromiseType = getGlobalPromiseType(); @@ -9168,13 +9411,21 @@ namespace ts { } function getReturnTypeFromBody(func: FunctionLikeDeclaration, contextualMapper?: TypeMapper): Type { + let type: Type; + if (func.parserContextFlags & ParserContextFlags.JavaScriptFile) { + type = getReturnTypeFromJSDocComment(func); + if (type && type !== unknownType) { + return type; + } + } + let contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func); if (!func.body) { return unknownType; } let isAsync = isAsyncFunctionLike(func); - let type: Type; + if (func.body.kind !== SyntaxKind.Block) { type = checkExpressionCached(func.body, contextualMapper); if (isAsync) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 26484bfe97a2f..a9e5aa6f9c602 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2011,11 +2011,11 @@ namespace ts { } export function isJavaScript(fileName: string) { - return fileExtensionIs(fileName, ".js"); + return fileExtensionIs(fileName, ".js") || fileExtensionIs(fileName, ".jsx"); } export function isTsx(fileName: string) { - return fileExtensionIs(fileName, ".tsx"); + return fileExtensionIs(fileName, ".tsx") || fileExtensionIs(fileName, ".jsx"); } /** diff --git a/src/harness/harness.ts b/src/harness/harness.ts index e37a8610089b0..c833e6031315a 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -201,7 +201,7 @@ module Utils { return isNodeOrArray(v) ? serializeNode(v) : v; }, " "); - function getKindName(k: number | string): string { + function getKindName(k: number|string): string { if (typeof k === "string") { return k; } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 7eaf944e91666..54045b714affb 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -963,7 +963,9 @@ namespace ts.server { this.setCompilerOptions(opt); } else { - this.setCompilerOptions(ts.getDefaultCompilerOptions()); + var defaultOpts = ts.getDefaultCompilerOptions(); + defaultOpts.allowNonTsExtensions = true; + this.setCompilerOptions(defaultOpts); } this.languageService = ts.createLanguageService(this.host, this.documentRegistry); this.classifier = ts.createClassifier(); diff --git a/src/services/services.ts b/src/services/services.ts index 07ba30f4e1dd9..519e4ee112140 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4075,16 +4075,18 @@ namespace ts { } else { // Method/function type parameter - let signatureDeclaration = getDeclarationOfKind(symbol, SyntaxKind.TypeParameter).parent; - let signature = typeChecker.getSignatureFromDeclaration(signatureDeclaration); - if (signatureDeclaration.kind === SyntaxKind.ConstructSignature) { - displayParts.push(keywordPart(SyntaxKind.NewKeyword)); - displayParts.push(spacePart()); - } - else if (signatureDeclaration.kind !== SyntaxKind.CallSignature && signatureDeclaration.name) { - addFullSymbolName(signatureDeclaration.symbol); + let signatureDeclaration = getTypeParameterOwner(getDeclarationOfKind(symbol, SyntaxKind.TypeParameter)); + if (signatureDeclaration) { + let signature = typeChecker.getSignatureFromDeclaration(signatureDeclaration); + if (signatureDeclaration.kind === SyntaxKind.ConstructSignature) { + displayParts.push(keywordPart(SyntaxKind.NewKeyword)); + displayParts.push(spacePart()); + } + else if (signatureDeclaration.kind !== SyntaxKind.CallSignature && signatureDeclaration.name) { + addFullSymbolName(signatureDeclaration.symbol); + } + addRange(displayParts, signatureToDisplayParts(typeChecker, signature, sourceFile, TypeFormatFlags.WriteTypeArgumentsOfSignature)); } - addRange(displayParts, signatureToDisplayParts(typeChecker, signature, sourceFile, TypeFormatFlags.WriteTypeArgumentsOfSignature)); } } if (symbolFlags & SymbolFlags.EnumMember) { @@ -6673,7 +6675,6 @@ namespace ts { return ClassificationType.parameterName; } return; - } } diff --git a/tests/cases/fourslash/getJavaScriptCompletions1.ts b/tests/cases/fourslash/getJavaScriptCompletions1.ts new file mode 100644 index 0000000000000..c4da79186c9b3 --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptCompletions1.ts @@ -0,0 +1,10 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** @type {number} */ +////var v; +////v./**/ + +goTo.marker(); +verify.completionListContains("toExponential", /*displayText:*/ undefined, /*documentation*/ undefined, "method"); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptCompletions10.ts b/tests/cases/fourslash/getJavaScriptCompletions10.ts new file mode 100644 index 0000000000000..5fbd91e89881a --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptCompletions10.ts @@ -0,0 +1,11 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** +//// * @type {function(this:number)} +//// */ +////function f() { this./**/ } + +goTo.marker(); +verify.completionListContains("toExponential", /*displayText:*/ undefined, /*documentation*/ undefined, "method"); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptCompletions11.ts b/tests/cases/fourslash/getJavaScriptCompletions11.ts new file mode 100644 index 0000000000000..f12f53fa960fe --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptCompletions11.ts @@ -0,0 +1,11 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** @type {number|string} */ +////var v; +////v./**/ + +goTo.marker(); +verify.completionListContains("toExponential", /*displayText:*/ undefined, /*documentation*/ undefined, "method"); +verify.completionListContains("charCodeAt", /*displayText:*/ undefined, /*documentation*/ undefined, "method"); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptCompletions2.ts b/tests/cases/fourslash/getJavaScriptCompletions2.ts new file mode 100644 index 0000000000000..0cb2de046a668 --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptCompletions2.ts @@ -0,0 +1,10 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** @type {(number|string)} */ +////var v; +////v./**/ + +goTo.marker(); +verify.completionListContains("valueOf", /*displayText:*/ undefined, /*documentation*/ undefined, "method"); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptCompletions3.ts b/tests/cases/fourslash/getJavaScriptCompletions3.ts new file mode 100644 index 0000000000000..13a6f1673e6b3 --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptCompletions3.ts @@ -0,0 +1,10 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** @type {Array.} */ +////var v; +////v./**/ + +goTo.marker(); +verify.completionListContains("concat", /*displayText:*/ undefined, /*documentation*/ undefined, "method"); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptCompletions4.ts b/tests/cases/fourslash/getJavaScriptCompletions4.ts new file mode 100644 index 0000000000000..3719b8e1258cb --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptCompletions4.ts @@ -0,0 +1,10 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** @return {number} */ +////function foo(a,b) { } +////foo(1,2)./**/ + +goTo.marker(); +verify.completionListContains("toExponential", /*displayText:*/ undefined, /*documentation*/ undefined, "method"); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptCompletions5.ts b/tests/cases/fourslash/getJavaScriptCompletions5.ts new file mode 100644 index 0000000000000..0cd0af6aab75c --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptCompletions5.ts @@ -0,0 +1,14 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** +//// * @template T +//// * @param {T} a +//// * @return {T} */ +////function foo(a) { } +////foo(1)./**/ + +debugger; +goTo.marker(); +verify.completionListContains("toExponential", /*displayText:*/ undefined, /*documentation*/ undefined, "method"); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptCompletions6.ts b/tests/cases/fourslash/getJavaScriptCompletions6.ts new file mode 100644 index 0000000000000..9f9a42b5762c2 --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptCompletions6.ts @@ -0,0 +1,13 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** +//// * @param {...number} a +//// */ +////function foo(a) { +//// a./**/ +////} + +goTo.marker(); +verify.completionListContains("concat", /*displayText:*/ undefined, /*documentation*/ undefined, "method"); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptCompletions7.ts b/tests/cases/fourslash/getJavaScriptCompletions7.ts new file mode 100644 index 0000000000000..d8b01cfb757c2 --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptCompletions7.ts @@ -0,0 +1,13 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** +//// * @param {...number} a +//// */ +////function foo(a) { +//// a[0]./**/ +////} + +goTo.marker(); +verify.completionListContains("toExponential", /*displayText:*/ undefined, /*documentation*/ undefined, "method"); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptCompletions8.ts b/tests/cases/fourslash/getJavaScriptCompletions8.ts new file mode 100644 index 0000000000000..418aae676bee5 --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptCompletions8.ts @@ -0,0 +1,12 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** +//// * @type {function(): number} +//// */ +////var v; +////v()./**/ + +goTo.marker(); +verify.completionListContains("toExponential", /*displayText:*/ undefined, /*documentation*/ undefined, "method"); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptCompletions9.ts b/tests/cases/fourslash/getJavaScriptCompletions9.ts new file mode 100644 index 0000000000000..1619a2ca9fc49 --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptCompletions9.ts @@ -0,0 +1,12 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** +//// * @type {function(new:number)} +//// */ +////var v; +////new v()./**/ + +goTo.marker(); +verify.completionListContains("toExponential", /*displayText:*/ undefined, /*documentation*/ undefined, "method"); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptQuickInfo1.ts b/tests/cases/fourslash/getJavaScriptQuickInfo1.ts new file mode 100644 index 0000000000000..71072f1699aa2 --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptQuickInfo1.ts @@ -0,0 +1,9 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** @type {function(new:string,number)} */ +////var /**/v; + +goTo.marker(); +verify.quickInfoIs('var v: new (p1: number) => string'); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptQuickInfo2.ts b/tests/cases/fourslash/getJavaScriptQuickInfo2.ts new file mode 100644 index 0000000000000..1da049ed5c897 --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptQuickInfo2.ts @@ -0,0 +1,10 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** @param {number} [a] */ +////function /**/f(a) { } + +debugger; +goTo.marker(); +verify.quickInfoIs('function f(a?: number): void'); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptQuickInfo3.ts b/tests/cases/fourslash/getJavaScriptQuickInfo3.ts new file mode 100644 index 0000000000000..213b3b4ab5e2f --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptQuickInfo3.ts @@ -0,0 +1,10 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** @param {number[]} [a] */ +////function /**/f(a) { } + +debugger; +goTo.marker(); +verify.quickInfoIs('function f(a?: number[]): void'); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptQuickInfo4.ts b/tests/cases/fourslash/getJavaScriptQuickInfo4.ts new file mode 100644 index 0000000000000..c2a1095d23573 --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptQuickInfo4.ts @@ -0,0 +1,10 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** @param {[number,string]} [a] */ +////function /**/f(a) { } + +debugger; +goTo.marker(); +verify.quickInfoIs('function f(a?: [number, string]): void'); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptQuickInfo5.ts b/tests/cases/fourslash/getJavaScriptQuickInfo5.ts new file mode 100644 index 0000000000000..12dd37834bc64 --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptQuickInfo5.ts @@ -0,0 +1,10 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** @param {{b:number}} [a] */ +////function /**/f(a) { } + +debugger; +goTo.marker(); +verify.quickInfoIs('function f(a?: {\n b: number;\n}): void'); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptQuickInfo6.ts b/tests/cases/fourslash/getJavaScriptQuickInfo6.ts new file mode 100644 index 0000000000000..2d23752fa636c --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptQuickInfo6.ts @@ -0,0 +1,10 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +/////** @type {function(this:number)} */ +////function f() { /**/this } + +debugger; +goTo.marker(); +verify.quickInfoIs('number'); \ No newline at end of file diff --git a/tests/cases/fourslash/getJavaScriptQuickInfo7.ts b/tests/cases/fourslash/getJavaScriptQuickInfo7.ts new file mode 100644 index 0000000000000..d8ecb1ec138d9 --- /dev/null +++ b/tests/cases/fourslash/getJavaScriptQuickInfo7.ts @@ -0,0 +1,11 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: Foo.js +//// function f(a,b) { } +//// /** @type {f} */ +//// var v/**/ + +debugger; +goTo.marker(); +verify.quickInfoIs('var v: (a: any, b: any) => void'); \ No newline at end of file diff --git a/tests/cases/fourslash/getQuickInfo5.ts b/tests/cases/fourslash/getQuickInfo5.ts new file mode 100644 index 0000000000000..509e9fd139561 --- /dev/null +++ b/tests/cases/fourslash/getQuickInfo5.ts @@ -0,0 +1,7 @@ +/// + +////function /**/f(a: {b: number}) { } + +debugger; +goTo.marker(); +verify.quickInfoIs('function f(a: {\n b: number;\n}): void'); \ No newline at end of file