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
This commit is contained in:
Andy 2017-11-15 13:04:08 -08:00 committed by GitHub
parent 3d05952719
commit 4b96edf72f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 336 additions and 125 deletions

View file

@ -2924,32 +2924,24 @@ namespace ts {
function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext): ParameterDeclaration {
const parameterDeclaration = getDeclarationOfKind<ParameterDeclaration>(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 && (<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(<ParameterDeclaration>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((<ParenthesizedTypeNode | JSDocTypeReferencingNode | JSDocTypeExpression>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(<JSDocVariadicType>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 && (<VariableLikeDeclaration>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);
}

View file

@ -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

View file

@ -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();
}

View file

@ -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 {

View file

@ -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);

View file

@ -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))

View file

@ -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

View file

@ -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.<number> = [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.

View file

@ -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[]

View file

@ -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) {
}

View file

@ -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
}

View file

@ -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);

View file

@ -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))

View file

@ -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

View file

@ -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))
}

View file

@ -0,0 +1,10 @@
=== /a.js ===
/** @param {...number} a */
function f(...a) {
>f : (...a: number[]) => void
>a : number[]
a; // number[]
>a : number[]
}

View file

@ -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);

View file

@ -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);

View file

@ -0,0 +1,10 @@
// @allowJs: true
// @checkJs: true
// @strict: true
// @noEmit: true
// @Filename: /a.js
/** @param {...number} a */
function f(...a) {
a; // number[]
}

View file

@ -1,10 +0,0 @@
// @strict: true
/// <reference path='fourslash.ts' />
////type T = [|...number?|];
verify.codeFix({
description: "Change '...number?' to 'number[] | null'.",
errorCode: 8020,
index: 0,
newRangeContent: "number[] | null",
});

View file

@ -1,7 +0,0 @@
/// <reference path='fourslash.ts' />
//// var x: [|......number[][]|] = 12;
verify.codeFix({
description: "Change '......number[][]' to 'number[][][][]'.",
newRangeContent: "number[][][][]",
});

View file

@ -1,13 +0,0 @@
///<reference path="fourslash.ts" />
// @allowNonTsExtensions: true
// @Filename: Foo.js
/////**
//// * @param {...number} a
//// */
////function foo(a) {
//// a./**/
////}
goTo.marker();
verify.completionListContains("concat", /*displayText:*/ undefined, /*documentation*/ undefined, "method");

View file

@ -1,13 +0,0 @@
///<reference path="fourslash.ts" />
// @allowNonTsExtensions: true
// @Filename: Foo.js
/////**
//// * @param {...number} a
//// */
////function foo(a) {
//// a[0]./**/
////}
goTo.marker();
verify.completionListContains("toExponential", /*displayText:*/ undefined, /*documentation*/ undefined, "method");