From 4b96edf72faf6f9b6b464e148c9c02eac96aa4bd Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 15 Nov 2017 13:04:08 -0800 Subject: [PATCH] Treat `...` in jsdoc type as creating a synthetic rest parameter -- not as an array type (#19483) * Treat `...` in jsdoc type as creating a synthetic rest parameter -- not as an array type * Change type parsing so `...T[]` parses as `...(T[])` and not `(...T)[]` * Replace the last parameter with ...args, and make access to it potentially undefined * Code review --- src/compiler/checker.ts | 176 ++++++++++++------ src/compiler/diagnosticMessages.json | 4 + src/compiler/parser.ts | 8 +- src/compiler/utilities.ts | 14 +- ...jsFileCompilationRestParamJsDocFunction.js | 8 +- ...eCompilationRestParamJsDocFunction.symbols | 2 +- ...ileCompilationRestParamJsDocFunction.types | 6 +- .../jsdocDisallowedInTypescript.errors.txt | 13 +- .../jsdocDisallowedInTypescript.types | 4 +- .../jsdocPrefixPostfixParsing.errors.txt | 43 +++++ .../reference/jsdocPrefixPostfixParsing.types | 18 +- .../reference/jsdocRestParameter.errors.txt | 19 ++ .../reference/jsdocRestParameter.symbols | 22 +++ .../reference/jsdocRestParameter.types | 34 ++++ .../reference/jsdocRestParameter_es6.symbols | 10 + .../reference/jsdocRestParameter_es6.types | 10 + ...jsFileCompilationRestParamJsDocFunction.ts | 2 +- tests/cases/compiler/jsdocRestParameter.ts | 15 ++ .../cases/compiler/jsdocRestParameter_es6.ts | 10 + .../fourslash/codeFixChangeJSDocSyntax27.ts | 10 - .../fourslash/codeFixChangeJSDocSyntax3.ts | 7 - .../fourslash/getJavaScriptCompletions6.ts | 13 -- .../fourslash/getJavaScriptCompletions7.ts | 13 -- 23 files changed, 336 insertions(+), 125 deletions(-) create mode 100644 tests/baselines/reference/jsdocPrefixPostfixParsing.errors.txt create mode 100644 tests/baselines/reference/jsdocRestParameter.errors.txt create mode 100644 tests/baselines/reference/jsdocRestParameter.symbols create mode 100644 tests/baselines/reference/jsdocRestParameter.types create mode 100644 tests/baselines/reference/jsdocRestParameter_es6.symbols create mode 100644 tests/baselines/reference/jsdocRestParameter_es6.types create mode 100644 tests/cases/compiler/jsdocRestParameter.ts create mode 100644 tests/cases/compiler/jsdocRestParameter_es6.ts delete mode 100644 tests/cases/fourslash/codeFixChangeJSDocSyntax27.ts delete mode 100644 tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts delete mode 100644 tests/cases/fourslash/getJavaScriptCompletions6.ts delete mode 100644 tests/cases/fourslash/getJavaScriptCompletions7.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5862a436f97..631960f24ba 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2924,32 +2924,24 @@ namespace ts { function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext): ParameterDeclaration { const parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter); - if (isTransientSymbol(parameterSymbol) && parameterSymbol.isRestParameter) { - // special-case synthetic rest parameters in JS files - return createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - parameterSymbol.isRestParameter ? createToken(SyntaxKind.DotDotDotToken) : undefined, - "args", - /*questionToken*/ undefined, - typeToTypeNodeHelper(anyArrayType, context), - /*initializer*/ undefined); - } - const modifiers = parameterDeclaration.modifiers && parameterDeclaration.modifiers.map(getSynthesizedClone); - const dotDotDotToken = isRestParameter(parameterDeclaration) ? createToken(SyntaxKind.DotDotDotToken) : undefined; - const name = parameterDeclaration.name ? - parameterDeclaration.name.kind === SyntaxKind.Identifier ? - setEmitFlags(getSynthesizedClone(parameterDeclaration.name), EmitFlags.NoAsciiEscaping) : - cloneBindingName(parameterDeclaration.name) : - symbolName(parameterSymbol); - const questionToken = isOptionalParameter(parameterDeclaration) ? createToken(SyntaxKind.QuestionToken) : undefined; + Debug.assert(!!parameterDeclaration || isTransientSymbol(parameterSymbol) && !!parameterSymbol.isRestParameter); let parameterType = getTypeOfSymbol(parameterSymbol); - if (isRequiredInitializedParameter(parameterDeclaration)) { - parameterType = getNullableType(parameterType, TypeFlags.Undefined); + if (parameterDeclaration && isRequiredInitializedParameter(parameterDeclaration)) { + parameterType = getOptionalType(parameterType); } const parameterTypeNode = typeToTypeNodeHelper(parameterType, context); + const modifiers = parameterDeclaration && parameterDeclaration.modifiers && parameterDeclaration.modifiers.map(getSynthesizedClone); + const dotDotDotToken = !parameterDeclaration || isRestParameter(parameterDeclaration) ? createToken(SyntaxKind.DotDotDotToken) : undefined; + const name = parameterDeclaration + ? parameterDeclaration.name ? + parameterDeclaration.name.kind === SyntaxKind.Identifier ? + setEmitFlags(getSynthesizedClone(parameterDeclaration.name), EmitFlags.NoAsciiEscaping) : + cloneBindingName(parameterDeclaration.name) : + symbolName(parameterSymbol) + : symbolName(parameterSymbol); + const questionToken = parameterDeclaration && isOptionalParameter(parameterDeclaration) ? createToken(SyntaxKind.QuestionToken) : undefined; const parameterNode = createParameter( /*decorators*/ undefined, modifiers, @@ -3717,7 +3709,7 @@ namespace ts { let type = getTypeOfSymbol(p); if (parameterNode && isRequiredInitializedParameter(parameterNode)) { - type = getNullableType(type, TypeFlags.Undefined); + type = getOptionalType(type); } buildTypeDisplay(type, writer, enclosingDeclaration, flags, symbolStack); } @@ -4288,8 +4280,8 @@ namespace ts { return expr.kind === SyntaxKind.ArrayLiteralExpression && (expr).elements.length === 0; } - function addOptionality(type: Type, optional: boolean): Type { - return strictNullChecks && optional ? getNullableType(type, TypeFlags.Undefined) : type; + function addOptionality(type: Type, optional = true): Type { + return strictNullChecks && optional ? getOptionalType(type) : type; } // Return the inferred type for a variable, parameter, or property declaration @@ -4318,7 +4310,7 @@ namespace ts { const typeNode = getEffectiveTypeAnnotationNode(declaration); if (typeNode) { const declaredType = getTypeFromTypeNode(typeNode); - return addOptionality(declaredType, /*optional*/ declaration.questionToken && includeOptionality); + return addOptionality(declaredType, /*optional*/ !!declaration.questionToken && includeOptionality); } if ((noImplicitAny || isInJavaScriptFile(declaration)) && @@ -4362,14 +4354,14 @@ namespace ts { type = getContextuallyTypedParameterType(declaration); } if (type) { - return addOptionality(type, /*optional*/ declaration.questionToken && includeOptionality); + return addOptionality(type, /*optional*/ !!declaration.questionToken && includeOptionality); } } // Use the type of the initializer expression if one is present if (declaration.initializer) { const type = checkDeclarationInitializer(declaration); - return addOptionality(type, /*optional*/ declaration.questionToken && includeOptionality); + return addOptionality(type, /*optional*/ !!declaration.questionToken && includeOptionality); } if (isJsxAttribute(declaration)) { @@ -4707,7 +4699,7 @@ namespace ts { links.type = baseTypeVariable ? getIntersectionType([type, baseTypeVariable]) : type; } else { - links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ? getNullableType(type, TypeFlags.Undefined) : type; + links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ? getOptionalType(type) : type; } } } @@ -6505,21 +6497,35 @@ namespace ts { const typePredicate = declaration.type && declaration.type.kind === SyntaxKind.TypePredicate ? createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode) : undefined; - // JS functions get a free rest parameter if they reference `arguments` - let hasRestLikeParameter = hasRestParameter(declaration); - if (!hasRestLikeParameter && isInJavaScriptFile(declaration) && containsArgumentsReference(declaration)) { - hasRestLikeParameter = true; - const syntheticArgsSymbol = createSymbol(SymbolFlags.Variable, "args" as __String); - syntheticArgsSymbol.type = anyArrayType; - syntheticArgsSymbol.isRestParameter = true; - parameters.push(syntheticArgsSymbol); - } - + const hasRestLikeParameter = hasRestParameter(declaration) || isInJavaScriptFile(declaration) && maybeAddJsSyntheticRestParameter(declaration, parameters); links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters, returnType, typePredicate, minArgumentCount, hasRestLikeParameter, hasLiteralTypes); } return links.resolvedSignature; } + function maybeAddJsSyntheticRestParameter(declaration: SignatureDeclaration, parameters: Symbol[]): boolean { + // JS functions get a free rest parameter if: + // a) The last parameter has `...` preceding its type + // b) It references `arguments` somewhere + const lastParam = lastOrUndefined(declaration.parameters); + const lastParamTags = lastParam && getJSDocParameterTags(lastParam); + const lastParamVariadicType = lastParamTags && firstDefined(lastParamTags, p => + p.typeExpression && isJSDocVariadicType(p.typeExpression.type) ? p.typeExpression.type : undefined); + if (!lastParamVariadicType && !containsArgumentsReference(declaration)) { + return false; + } + + const syntheticArgsSymbol = createSymbol(SymbolFlags.Variable, "args" as __String); + syntheticArgsSymbol.type = lastParamVariadicType ? createArrayType(getTypeFromTypeNode(lastParamVariadicType.type)) : anyArrayType; + syntheticArgsSymbol.isRestParameter = true; + if (lastParamVariadicType) { + // Replace the last parameter with a rest parameter. + parameters.pop(); + } + parameters.push(syntheticArgsSymbol); + return true; + } + function getSignatureReturnTypeFromDeclaration(declaration: SignatureDeclaration, isJSConstructSignature: boolean, classType: Type) { if (isJSConstructSignature) { return getTypeFromTypeNode(declaration.parameters[0].type); @@ -7074,7 +7080,7 @@ namespace ts { function getTypeFromJSDocNullableTypeNode(node: JSDocNullableType) { const type = getTypeFromTypeNode(node.type); - return strictNullChecks ? getUnionType([type, nullType]) : type; + return strictNullChecks ? getNullableType(type, TypeFlags.Null) : type; } function getTypeFromTypeReference(node: TypeReferenceType): Type { @@ -8048,15 +8054,6 @@ namespace ts { return links.resolvedType; } - function getTypeFromJSDocVariadicType(node: JSDocVariadicType): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - const type = getTypeFromTypeNode(node.type); - links.resolvedType = type ? createArrayType(type) : unknownType; - } - return links.resolvedType; - } - function getThisType(node: Node): Type { const container = getThisContainer(node, /*includeArrowFunctions*/ false); const parent = container && container.parent; @@ -8130,6 +8127,8 @@ namespace ts { case SyntaxKind.JSDocOptionalType: case SyntaxKind.JSDocTypeExpression: return getTypeFromTypeNode((node).type); + case SyntaxKind.JSDocVariadicType: + return getTypeFromJSDocVariadicType(node as JSDocVariadicType); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.TypeLiteral: @@ -8148,8 +8147,6 @@ namespace ts { case SyntaxKind.QualifiedName: const symbol = getSymbolAtLocation(node); return symbol && getDeclaredTypeOfSymbol(symbol); - case SyntaxKind.JSDocVariadicType: - return getTypeFromJSDocVariadicType(node); default: return unknownType; } @@ -10416,6 +10413,11 @@ namespace ts { getUnionType([type, undefinedType, nullType]); } + function getOptionalType(type: Type): Type { + Debug.assert(strictNullChecks); + return type.flags & TypeFlags.Undefined ? type : getUnionType([type, undefinedType]); + } + function getNonNullableType(type: Type): Type { return strictNullChecks ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type; } @@ -12765,7 +12767,7 @@ namespace ts { declaration.flags & NodeFlags.Ambient; const initialType = assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, getRootDeclaration(declaration) as VariableLikeDeclaration) : type) : type === autoType || type === autoArrayType ? undefinedType : - getNullableType(type, TypeFlags.Undefined); + getOptionalType(type); const flowType = getFlowTypeOfReference(node, type, initialType, flowContainer, !assumeInitialized); // A variable is considered uninitialized when it is possible to analyze the entire control flow graph // from declaration to use, and when the variable's declared type doesn't include undefined but the @@ -17211,7 +17213,7 @@ namespace ts { if (strictNullChecks) { const declaration = symbol.valueDeclaration; if (declaration && (declaration).initializer) { - return getNullableType(type, TypeFlags.Undefined); + return getOptionalType(type); } } return type; @@ -23060,14 +23062,15 @@ namespace ts { case SyntaxKind.JSDocFunctionType: checkSignatureDeclaration(node as JSDocFunctionType); // falls through - case SyntaxKind.JSDocVariadicType: case SyntaxKind.JSDocNonNullableType: case SyntaxKind.JSDocNullableType: case SyntaxKind.JSDocAllType: case SyntaxKind.JSDocUnknownType: - if (!isInJavaScriptFile(node) && !isInJSDoc(node)) { - grammarErrorOnNode(node, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments); - } + checkJSDocTypeIsInJsFile(node); + forEachChild(node, checkSourceElement); + return; + case SyntaxKind.JSDocVariadicType: + checkJSDocVariadicType(node as JSDocVariadicType); return; case SyntaxKind.JSDocTypeExpression: return checkSourceElement((node as JSDocTypeExpression).type); @@ -23144,6 +23147,65 @@ namespace ts { } } + function checkJSDocTypeIsInJsFile(node: Node): void { + if (!isInJavaScriptFile(node)) { + grammarErrorOnNode(node, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments); + } + } + + function checkJSDocVariadicType(node: JSDocVariadicType): void { + checkJSDocTypeIsInJsFile(node); + checkSourceElement(node.type); + + // Only legal location is in the *last* parameter tag. + const { parent } = node; + if (!isJSDocTypeExpression(parent)) { + error(node, Diagnostics.JSDoc_may_only_appear_in_the_last_parameter_of_a_signature); + } + + const paramTag = parent.parent; + if (!isJSDocParameterTag(paramTag)) { + error(node, Diagnostics.JSDoc_may_only_appear_in_the_last_parameter_of_a_signature); + return; + } + + const param = getParameterSymbolFromJSDoc(paramTag); + if (!param) { + // We will error in `checkJSDocParameterTag`. + return; + } + + const host = getHostSignatureFromJSDoc(paramTag); + if (!host || last(host.parameters).symbol !== param) { + error(node, Diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list); + } + } + + function getTypeFromJSDocVariadicType(node: JSDocVariadicType): Type { + const type = getTypeFromTypeNode(node.type); + const { parent } = node; + const paramTag = parent.parent; + if (isJSDocTypeExpression(parent) && isJSDocParameterTag(paramTag)) { + // Else we will add a diagnostic, see `checkJSDocVariadicType`. + const param = getParameterSymbolFromJSDoc(paramTag); + if (param) { + const host = getHostSignatureFromJSDoc(paramTag); + /* + Only return an array type if the corresponding parameter is marked as a rest parameter. + So in the following situation we will not create an array type: + /** @param {...number} a * / + function f(a) {} + Because `a` will just be of type `number | undefined`. A synthetic `...args` will also be added, which *will* get an array type. + */ + const lastParamDeclaration = host && last(host.parameters); + if (lastParamDeclaration.symbol === param && isRestParameter(lastParamDeclaration)) { + return createArrayType(type); + } + } + } + return addOptionality(type); + } + // Function and class expression bodies are checked after all statements in the enclosing body. This is // to ensure constructs like the following are permitted: // const foo = function () { @@ -24272,7 +24334,7 @@ namespace ts { ? getWidenedLiteralType(getTypeOfSymbol(symbol)) : unknownType; if (flags & TypeFormatFlags.AddUndefined) { - type = getNullableType(type, TypeFlags.Undefined); + type = getOptionalType(type); } getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags); } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 964264ff6e8..a552122609a 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3547,6 +3547,10 @@ "category": "Error", "code": 8027 }, + "JSDoc '...' may only appear in the last parameter of a signature.": { + "category": "Error", + "code": 8028 + }, "Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": { "category": "Error", "code": 9002 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 5456e889353..6f1a9e1a561 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2678,8 +2678,6 @@ namespace ts { return parseJSDocUnknownOrNullableType(); case SyntaxKind.FunctionKeyword: return parseJSDocFunctionType(); - case SyntaxKind.DotDotDotToken: - return parseJSDocNodeWithType(SyntaxKind.JSDocVariadicType); case SyntaxKind.ExclamationToken: return parseJSDocNodeWithType(SyntaxKind.JSDocNonNullableType); case SyntaxKind.NoSubstitutionTemplateLiteral: @@ -2819,6 +2817,12 @@ namespace ts { switch (token()) { case SyntaxKind.KeyOfKeyword: return parseTypeOperator(SyntaxKind.KeyOfKeyword); + case SyntaxKind.DotDotDotToken: { + const result = createNode(SyntaxKind.JSDocVariadicType) as JSDocVariadicType; + nextToken(); + result.type = parsePostfixTypeOrHigher(); + return finishNode(result); + } } return parsePostfixTypeOrHigher(); } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index e488cdcc601..1857ca10bd2 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1626,16 +1626,22 @@ namespace ts { return undefined; } const name = node.name.escapedText; + const decl = getHostSignatureFromJSDoc(node); + if (!decl) { + return undefined; + } + const parameter = find(decl.parameters, p => p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name); + return parameter && parameter.symbol; + } + + export function getHostSignatureFromJSDoc(node: JSDocParameterTag): FunctionLike | undefined { const host = getJSDocHost(node); const decl = getSourceOfAssignment(host) || getSingleInitializerOfVariableStatement(host) || getSingleVariableOfVariableStatement(host) || getNestedModuleDeclaration(host) || host; - if (decl && isFunctionLike(decl)) { - const parameter = find(decl.parameters, p => p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name); - return parameter && parameter.symbol; - } + return decl && isFunctionLike(decl) ? decl : undefined; } export function getJSDocHost(node: JSDocTag): HasJSDoc { diff --git a/tests/baselines/reference/jsFileCompilationRestParamJsDocFunction.js b/tests/baselines/reference/jsFileCompilationRestParamJsDocFunction.js index acc590418e9..fbe8df0c1c4 100644 --- a/tests/baselines/reference/jsFileCompilationRestParamJsDocFunction.js +++ b/tests/baselines/reference/jsFileCompilationRestParamJsDocFunction.js @@ -9,7 +9,7 @@ * @param {...*} args The arguments to invoke `func` with. * @returns {*} Returns the result of `func`. */ -function apply(func, thisArg, args) { +function apply(func, thisArg, ...args) { var length = args.length; switch (length) { case 0: return func.call(thisArg); @@ -36,7 +36,11 @@ define("_apply", ["require", "exports"], function (require, exports) { * @param {...*} args The arguments to invoke `func` with. * @returns {*} Returns the result of `func`. */ - function apply(func, thisArg, args) { + function apply(func, thisArg) { + var args = []; + for (var _i = 2; _i < arguments.length; _i++) { + args[_i - 2] = arguments[_i]; + } var length = args.length; switch (length) { case 0: return func.call(thisArg); diff --git a/tests/baselines/reference/jsFileCompilationRestParamJsDocFunction.symbols b/tests/baselines/reference/jsFileCompilationRestParamJsDocFunction.symbols index e7589f6ce55..fc70ae01a6c 100644 --- a/tests/baselines/reference/jsFileCompilationRestParamJsDocFunction.symbols +++ b/tests/baselines/reference/jsFileCompilationRestParamJsDocFunction.symbols @@ -9,7 +9,7 @@ * @param {...*} args The arguments to invoke `func` with. * @returns {*} Returns the result of `func`. */ -function apply(func, thisArg, args) { +function apply(func, thisArg, ...args) { >apply : Symbol(apply, Decl(_apply.js, 0, 0)) >func : Symbol(func, Decl(_apply.js, 10, 15)) >thisArg : Symbol(thisArg, Decl(_apply.js, 10, 20)) diff --git a/tests/baselines/reference/jsFileCompilationRestParamJsDocFunction.types b/tests/baselines/reference/jsFileCompilationRestParamJsDocFunction.types index 72c3388f226..c0247bb045f 100644 --- a/tests/baselines/reference/jsFileCompilationRestParamJsDocFunction.types +++ b/tests/baselines/reference/jsFileCompilationRestParamJsDocFunction.types @@ -9,8 +9,8 @@ * @param {...*} args The arguments to invoke `func` with. * @returns {*} Returns the result of `func`. */ -function apply(func, thisArg, args) { ->apply : (func: Function, thisArg: any, args: any[]) => any +function apply(func, thisArg, ...args) { +>apply : (func: Function, thisArg: any, ...args: any[]) => any >func : Function >thisArg : any >args : any[] @@ -84,5 +84,5 @@ function apply(func, thisArg, args) { } export default apply; ->apply : (func: Function, thisArg: any, args: any[]) => any +>apply : (func: Function, thisArg: any, ...args: any[]) => any diff --git a/tests/baselines/reference/jsdocDisallowedInTypescript.errors.txt b/tests/baselines/reference/jsdocDisallowedInTypescript.errors.txt index d176e1aa22e..2823981cc95 100644 --- a/tests/baselines/reference/jsdocDisallowedInTypescript.errors.txt +++ b/tests/baselines/reference/jsdocDisallowedInTypescript.errors.txt @@ -7,7 +7,10 @@ tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(11,12): error TS255 tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(13,14): error TS8020: JSDoc types can only be used inside documentation comments. tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(14,11): error TS8020: JSDoc types can only be used inside documentation comments. tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(15,8): error TS8020: JSDoc types can only be used inside documentation comments. +tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(16,5): error TS2322: Type 'boolean[]' is not assignable to type 'boolean | undefined'. + Type 'boolean[]' is not assignable to type 'false'. tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(16,15): error TS8020: JSDoc types can only be used inside documentation comments. +tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(16,15): error TS8028: JSDoc '...' may only appear in the last parameter of a signature. tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(17,11): error TS8020: JSDoc types can only be used inside documentation comments. tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(18,17): error TS8020: JSDoc types can only be used inside documentation comments. tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(19,5): error TS2322: Type 'undefined' is not assignable to type 'number | null'. @@ -16,9 +19,10 @@ tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(21,16): error TS802 tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(22,16): error TS8020: JSDoc types can only be used inside documentation comments. tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(23,17): error TS8020: JSDoc types can only be used inside documentation comments. tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(24,17): error TS8020: JSDoc types can only be used inside documentation comments. +tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(24,17): error TS8028: JSDoc '...' may only appear in the last parameter of a signature. -==== tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts (18 errors) ==== +==== tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts (21 errors) ==== // grammar error from checker var ara: Array. = [1,2,3]; ~ @@ -53,8 +57,13 @@ tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(24,17): error TS802 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS8020: JSDoc types can only be used inside documentation comments. var variadic: ...boolean = [true, false, true]; + ~~~~~~~~ +!!! error TS2322: Type 'boolean[]' is not assignable to type 'boolean | undefined'. +!!! error TS2322: Type 'boolean[]' is not assignable to type 'false'. ~~~~~~~~~~ !!! error TS8020: JSDoc types can only be used inside documentation comments. + ~~~~~~~~~~ +!!! error TS8028: JSDoc '...' may only appear in the last parameter of a signature. var most: !string = 'definite'; ~~~~~~~ !!! error TS8020: JSDoc types can only be used inside documentation comments. @@ -79,5 +88,7 @@ tests/cases/conformance/jsdoc/jsdocDisallowedInTypescript.ts(24,17): error TS802 var vars: Array<...number>; ~~~~~~~~~ !!! error TS8020: JSDoc types can only be used inside documentation comments. + ~~~~~~~~~ +!!! error TS8028: JSDoc '...' may only appear in the last parameter of a signature. \ No newline at end of file diff --git a/tests/baselines/reference/jsdocDisallowedInTypescript.types b/tests/baselines/reference/jsdocDisallowedInTypescript.types index 49f16acfd19..8fe4a032f20 100644 --- a/tests/baselines/reference/jsdocDisallowedInTypescript.types +++ b/tests/baselines/reference/jsdocDisallowedInTypescript.types @@ -65,7 +65,7 @@ var g: function(number, number): number = (n,m) => n + m; >m : number var variadic: ...boolean = [true, false, true]; ->variadic : boolean[] +>variadic : boolean | undefined >[true, false, true] : boolean[] >true : true >false : false @@ -96,7 +96,7 @@ var anys: Array<*>; >Array : T[] var vars: Array<...number>; ->vars : number[][] +>vars : (number | undefined)[] >Array : T[] diff --git a/tests/baselines/reference/jsdocPrefixPostfixParsing.errors.txt b/tests/baselines/reference/jsdocPrefixPostfixParsing.errors.txt new file mode 100644 index 00000000000..2b3d0b1647c --- /dev/null +++ b/tests/baselines/reference/jsdocPrefixPostfixParsing.errors.txt @@ -0,0 +1,43 @@ +tests/cases/conformance/jsdoc/prefixPostfix.js(8,13): error TS8028: JSDoc '...' may only appear in the last parameter of a signature. +tests/cases/conformance/jsdoc/prefixPostfix.js(9,12): error TS1014: A rest parameter must be last in a parameter list. +tests/cases/conformance/jsdoc/prefixPostfix.js(10,12): error TS1014: A rest parameter must be last in a parameter list. +tests/cases/conformance/jsdoc/prefixPostfix.js(11,12): error TS1014: A rest parameter must be last in a parameter list. +tests/cases/conformance/jsdoc/prefixPostfix.js(12,12): error TS1014: A rest parameter must be last in a parameter list. +tests/cases/conformance/jsdoc/prefixPostfix.js(13,12): error TS1014: A rest parameter must be last in a parameter list. +tests/cases/conformance/jsdoc/prefixPostfix.js(14,12): error TS1014: A rest parameter must be last in a parameter list. + + +==== tests/cases/conformance/jsdoc/prefixPostfix.js (7 errors) ==== + /** + * @param {number![]} x - number[] + * @param {!number[]} y - number[] + * @param {(number[])!} z - number[] + * @param {number?[]} a - (number | null)[] + * @param {?number[]} b - number[] | null + * @param {(number[])?} c - number[] | null + * @param {?...number} d - number[] | null + ~~~~~~~~~ +!!! error TS8028: JSDoc '...' may only appear in the last parameter of a signature. + * @param {...?number} e - (number | null)[] + ~~~~~~~~~~ +!!! error TS1014: A rest parameter must be last in a parameter list. + * @param {...number?} f - number[] | null + ~~~~~~~~~~ +!!! error TS1014: A rest parameter must be last in a parameter list. + * @param {...number!?} g - number[] | null + ~~~~~~~~~~~ +!!! error TS1014: A rest parameter must be last in a parameter list. + * @param {...number?!} h - number[] | null + ~~~~~~~~~~~ +!!! error TS1014: A rest parameter must be last in a parameter list. + * @param {...number[]} i - number[][] + ~~~~~~~~~~~ +!!! error TS1014: A rest parameter must be last in a parameter list. + * @param {...number![]?} j - number[][] | null + ~~~~~~~~~~~~~ +!!! error TS1014: A rest parameter must be last in a parameter list. + * @param {...number?[]!} k - (number[] | null)[] + */ + function f(x, y, z, a, b, c, d, e, f, g, h, i, j, k) { + } + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocPrefixPostfixParsing.types b/tests/baselines/reference/jsdocPrefixPostfixParsing.types index 9961af48e27..cf69cf9d40c 100644 --- a/tests/baselines/reference/jsdocPrefixPostfixParsing.types +++ b/tests/baselines/reference/jsdocPrefixPostfixParsing.types @@ -16,20 +16,20 @@ * @param {...number?[]!} k - (number[] | null)[] */ function f(x, y, z, a, b, c, d, e, f, g, h, i, j, k) { ->f : (x: number[], y: number[], z: number[], a: (number | null)[], b: number[] | null, c: number[] | null, d: number[] | null, e: (number | null)[], f: number[] | null, g: number[] | null, h: number[] | null, i: number[][], j: number[][] | null, k: (number[] | null)[]) => void +>f : (x: number[], y: number[], z: number[], a: (number | null)[], b: number[] | null, c: number[] | null, d: number | null | undefined, e: number | null | undefined, f: number | null | undefined, g: number | null | undefined, h: number | null | undefined, i: number[] | undefined, j: number[] | null | undefined, ...args: (number | null)[][]) => void >x : number[] >y : number[] >z : number[] >a : (number | null)[] >b : number[] | null >c : number[] | null ->d : number[] | null ->e : (number | null)[] ->f : number[] | null ->g : number[] | null ->h : number[] | null ->i : number[][] ->j : number[][] | null ->k : (number[] | null)[] +>d : number | null | undefined +>e : number | null | undefined +>f : number | null | undefined +>g : number | null | undefined +>h : number | null | undefined +>i : number[] | undefined +>j : number[] | null | undefined +>k : (number | null)[] | undefined } diff --git a/tests/baselines/reference/jsdocRestParameter.errors.txt b/tests/baselines/reference/jsdocRestParameter.errors.txt new file mode 100644 index 00000000000..ec34bcdcb20 --- /dev/null +++ b/tests/baselines/reference/jsdocRestParameter.errors.txt @@ -0,0 +1,19 @@ +/a.js(7,3): error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'number'. +/a.js(8,6): error TS2345: Argument of type '"2"' is not assignable to parameter of type 'number'. + + +==== /a.js (2 errors) ==== + /** @param {...number} a */ + function f(a) { + a; // number | undefined + // Ideally this would be a number. But currently checker.ts has only one `argumentsSymbol`, so it's `any`. + arguments[0]; + } + f([1, 2]); // Error + ~~~~~~ +!!! error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'number'. + f(1, "2"); // Error + ~~~ +!!! error TS2345: Argument of type '"2"' is not assignable to parameter of type 'number'. + f(1, 2); + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocRestParameter.symbols b/tests/baselines/reference/jsdocRestParameter.symbols new file mode 100644 index 00000000000..24e7c4635e9 --- /dev/null +++ b/tests/baselines/reference/jsdocRestParameter.symbols @@ -0,0 +1,22 @@ +=== /a.js === +/** @param {...number} a */ +function f(a) { +>f : Symbol(f, Decl(a.js, 0, 0)) +>a : Symbol(a, Decl(a.js, 1, 11)) + + a; // number | undefined +>a : Symbol(a, Decl(a.js, 1, 11)) + + // Ideally this would be a number. But currently checker.ts has only one `argumentsSymbol`, so it's `any`. + arguments[0]; +>arguments : Symbol(arguments) +} +f([1, 2]); // Error +>f : Symbol(f, Decl(a.js, 0, 0)) + +f(1, "2"); // Error +>f : Symbol(f, Decl(a.js, 0, 0)) + +f(1, 2); +>f : Symbol(f, Decl(a.js, 0, 0)) + diff --git a/tests/baselines/reference/jsdocRestParameter.types b/tests/baselines/reference/jsdocRestParameter.types new file mode 100644 index 00000000000..51b12c37fad --- /dev/null +++ b/tests/baselines/reference/jsdocRestParameter.types @@ -0,0 +1,34 @@ +=== /a.js === +/** @param {...number} a */ +function f(a) { +>f : (...args: number[]) => void +>a : number | undefined + + a; // number | undefined +>a : number | undefined + + // Ideally this would be a number. But currently checker.ts has only one `argumentsSymbol`, so it's `any`. + arguments[0]; +>arguments[0] : any +>arguments : IArguments +>0 : 0 +} +f([1, 2]); // Error +>f([1, 2]) : void +>f : (...args: number[]) => void +>[1, 2] : number[] +>1 : 1 +>2 : 2 + +f(1, "2"); // Error +>f(1, "2") : void +>f : (...args: number[]) => void +>1 : 1 +>"2" : "2" + +f(1, 2); +>f(1, 2) : void +>f : (...args: number[]) => void +>1 : 1 +>2 : 2 + diff --git a/tests/baselines/reference/jsdocRestParameter_es6.symbols b/tests/baselines/reference/jsdocRestParameter_es6.symbols new file mode 100644 index 00000000000..c82eaad37a2 --- /dev/null +++ b/tests/baselines/reference/jsdocRestParameter_es6.symbols @@ -0,0 +1,10 @@ +=== /a.js === +/** @param {...number} a */ +function f(...a) { +>f : Symbol(f, Decl(a.js, 0, 0)) +>a : Symbol(a, Decl(a.js, 1, 11)) + + a; // number[] +>a : Symbol(a, Decl(a.js, 1, 11)) +} + diff --git a/tests/baselines/reference/jsdocRestParameter_es6.types b/tests/baselines/reference/jsdocRestParameter_es6.types new file mode 100644 index 00000000000..b07d7d931f1 --- /dev/null +++ b/tests/baselines/reference/jsdocRestParameter_es6.types @@ -0,0 +1,10 @@ +=== /a.js === +/** @param {...number} a */ +function f(...a) { +>f : (...a: number[]) => void +>a : number[] + + a; // number[] +>a : number[] +} + diff --git a/tests/cases/compiler/jsFileCompilationRestParamJsDocFunction.ts b/tests/cases/compiler/jsFileCompilationRestParamJsDocFunction.ts index 03ad2d1b2ff..3588a739948 100644 --- a/tests/cases/compiler/jsFileCompilationRestParamJsDocFunction.ts +++ b/tests/cases/compiler/jsFileCompilationRestParamJsDocFunction.ts @@ -13,7 +13,7 @@ * @param {...*} args The arguments to invoke `func` with. * @returns {*} Returns the result of `func`. */ -function apply(func, thisArg, args) { +function apply(func, thisArg, ...args) { var length = args.length; switch (length) { case 0: return func.call(thisArg); diff --git a/tests/cases/compiler/jsdocRestParameter.ts b/tests/cases/compiler/jsdocRestParameter.ts new file mode 100644 index 00000000000..e67ce1faafc --- /dev/null +++ b/tests/cases/compiler/jsdocRestParameter.ts @@ -0,0 +1,15 @@ +// @allowJs: true +// @checkJs: true +// @strict: true +// @noEmit: true + +// @Filename: /a.js +/** @param {...number} a */ +function f(a) { + a; // number | undefined + // Ideally this would be a number. But currently checker.ts has only one `argumentsSymbol`, so it's `any`. + arguments[0]; +} +f([1, 2]); // Error +f(1, "2"); // Error +f(1, 2); diff --git a/tests/cases/compiler/jsdocRestParameter_es6.ts b/tests/cases/compiler/jsdocRestParameter_es6.ts new file mode 100644 index 00000000000..0a4db35c998 --- /dev/null +++ b/tests/cases/compiler/jsdocRestParameter_es6.ts @@ -0,0 +1,10 @@ +// @allowJs: true +// @checkJs: true +// @strict: true +// @noEmit: true + +// @Filename: /a.js +/** @param {...number} a */ +function f(...a) { + a; // number[] +} diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax27.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax27.ts deleted file mode 100644 index 998a9ebd28a..00000000000 --- a/tests/cases/fourslash/codeFixChangeJSDocSyntax27.ts +++ /dev/null @@ -1,10 +0,0 @@ -// @strict: true -/// -////type T = [|...number?|]; - -verify.codeFix({ - description: "Change '...number?' to 'number[] | null'.", - errorCode: 8020, - index: 0, - newRangeContent: "number[] | null", -}); diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts deleted file mode 100644 index 2c804edb615..00000000000 --- a/tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts +++ /dev/null @@ -1,7 +0,0 @@ -/// -//// var x: [|......number[][]|] = 12; - -verify.codeFix({ - description: "Change '......number[][]' to 'number[][][][]'.", - newRangeContent: "number[][][][]", -}); diff --git a/tests/cases/fourslash/getJavaScriptCompletions6.ts b/tests/cases/fourslash/getJavaScriptCompletions6.ts deleted file mode 100644 index 9f9a42b5762..00000000000 --- a/tests/cases/fourslash/getJavaScriptCompletions6.ts +++ /dev/null @@ -1,13 +0,0 @@ -/// - -// @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 deleted file mode 100644 index d8b01cfb757..00000000000 --- a/tests/cases/fourslash/getJavaScriptCompletions7.ts +++ /dev/null @@ -1,13 +0,0 @@ -/// - -// @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