Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/lib/es5.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,16 @@ interface CallableFunction extends Function {
*/
apply<T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, args: A): R;

/**
* Calls the function with the specified object as the this value and the elements of the specified
* array-like object as the arguments. Real arrays and tuples are matched by the overload above, which
* checks their arity against the parameter list; this overload additionally accepts non-array
* array-likes (e.g. `arguments`, typed arrays, DOM collections) as permitted by the ECMAScript spec.
* @param thisArg The object to be used as the this object.
* @param args An array-like of argument values to be passed to the function.
*/
apply<T, A extends any[], R, Args extends ArrayLike<A[number]>>(this: (this: T, ...args: A) => R, thisArg: T, args: Args extends readonly any[] ? A : Args): R;

/**
* Calls the function with the specified object as the this value and the specified rest arguments as the arguments.
* @param thisArg The object to be used as the this object.
Expand Down
91 changes: 91 additions & 0 deletions tests/baselines/reference/applyArrayLike.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
applyArrayLike.ts(25,34): error TS2769: No overload matches this call.
Overload 1 of 3, '(this: (this: undefined, a: number, b: string) => string, thisArg: undefined, args: [a: number, b: string]): string', gave the following error.
Argument of type '[number]' is not assignable to parameter of type '[a: number, b: string]'.
Source has 1 element(s) but target requires 2.
Overload 2 of 3, '(this: (this: undefined, a: number, b: string) => string, thisArg: undefined, args: [a: number, b: string]): string', gave the following error.
Argument of type '[number]' is not assignable to parameter of type '[a: number, b: string]'.
Source has 1 element(s) but target requires 2.
applyArrayLike.ts(26,39): error TS2769: No overload matches this call.
Overload 1 of 3, '(this: (this: undefined, a: number, b: string) => string, thisArg: undefined, args: [a: number, b: string]): string', gave the following error.
Type 'number' is not assignable to type 'string'.
Overload 2 of 3, '(this: (this: undefined, a: number, b: string) => string, thisArg: undefined, args: [a: number, b: string]): string', gave the following error.
Type 'number' is not assignable to type 'string'.
applyArrayLike.ts(27,34): error TS2769: No overload matches this call.
Overload 1 of 3, '(this: (this: undefined, a: number, b: string) => string, thisArg: undefined, args: [a: number, b: string]): string', gave the following error.
Argument of type '[number, string, number]' is not assignable to parameter of type '[a: number, b: string]'.
Source has 3 element(s) but target allows only 2.
Overload 2 of 3, '(this: (this: undefined, a: number, b: string) => string, thisArg: undefined, args: [a: number, b: string]): string', gave the following error.
Argument of type '[number, string, number]' is not assignable to parameter of type '[a: number, b: string]'.
Source has 3 element(s) but target allows only 2.
applyArrayLike.ts(31,45): error TS2769: No overload matches this call.
Overload 1 of 3, '(this: (this: null, ...args: number[]) => string, thisArg: null, args: number[]): string', gave the following error.
Argument of type 'ArrayLike<string>' is not assignable to parameter of type 'number[]'.
Type 'ArrayLike<string>' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.
Overload 2 of 3, '(this: (this: null, ...args: number[]) => string, thisArg: null, args: ArrayLike<number>): string', gave the following error.
Argument of type 'ArrayLike<string>' is not assignable to parameter of type 'ArrayLike<number>'.
Type 'string' is not assignable to type 'number'.


==== applyArrayLike.ts (4 errors) ====
// Repro for #61835: Function.prototype.apply accepts an array-like second argument
// per the ECMAScript spec (CreateListFromArrayLike), so CallableFunction.apply must
// accept ArrayLike<T>, not just T[]. These cases work at runtime but previously errored.

// --- Array-likes that should now be accepted ---

declare const u8: Uint8Array;
const s1: string = String.fromCharCode.apply(null, u8); // Ok (Uint8Array is ArrayLike<number>)

const forwardArguments: (...args: unknown[]) => void = function () {
console.log.apply(null, arguments); // Ok (IArguments is array-like)
};
forwardArguments(1, 2, 3);

declare function variadic(...xs: number[]): void;
declare const arrayLikeNumbers: ArrayLike<number>;
variadic.apply(undefined, arrayLikeNumbers); // Ok

// A plain array/tuple still works via the arity-checked overload.
declare function foo(a: number, b: string): string;
const a00 = foo.apply(undefined, [10, "hello"]); // Ok

// --- Regression guards: strictBindCallApply arity/element checks must be preserved ---

const a01 = foo.apply(undefined, [10]); // Error: too few elements
~~~~
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 3, '(this: (this: undefined, a: number, b: string) => string, thisArg: undefined, args: [a: number, b: string]): string', gave the following error.
!!! error TS2769: Argument of type '[number]' is not assignable to parameter of type '[a: number, b: string]'.
!!! error TS2769: Source has 1 element(s) but target requires 2.
!!! error TS2769: Overload 2 of 3, '(this: (this: undefined, a: number, b: string) => string, thisArg: undefined, args: [a: number, b: string]): string', gave the following error.
!!! error TS2769: Argument of type '[number]' is not assignable to parameter of type '[a: number, b: string]'.
!!! error TS2769: Source has 1 element(s) but target requires 2.
const a02 = foo.apply(undefined, [10, 20]); // Error: wrong element type
~~
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 3, '(this: (this: undefined, a: number, b: string) => string, thisArg: undefined, args: [a: number, b: string]): string', gave the following error.
!!! error TS2769: Type 'number' is not assignable to type 'string'.
!!! error TS2769: Overload 2 of 3, '(this: (this: undefined, a: number, b: string) => string, thisArg: undefined, args: [a: number, b: string]): string', gave the following error.
!!! error TS2769: Type 'number' is not assignable to type 'string'.
const a03 = foo.apply(undefined, [10, "hello", 30]); // Error: too many elements
~~~~~~~~~~~~~~~~~
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 3, '(this: (this: undefined, a: number, b: string) => string, thisArg: undefined, args: [a: number, b: string]): string', gave the following error.
!!! error TS2769: Argument of type '[number, string, number]' is not assignable to parameter of type '[a: number, b: string]'.
!!! error TS2769: Source has 3 element(s) but target allows only 2.
!!! error TS2769: Overload 2 of 3, '(this: (this: undefined, a: number, b: string) => string, thisArg: undefined, args: [a: number, b: string]): string', gave the following error.
!!! error TS2769: Argument of type '[number, string, number]' is not assignable to parameter of type '[a: number, b: string]'.
!!! error TS2769: Source has 3 element(s) but target allows only 2.

// An array-like of the wrong element type must still be rejected.
declare const arrayLikeStrings: ArrayLike<string>;
const bad = String.fromCharCode.apply(null, arrayLikeStrings); // Error: string is not number
~~~~~~~~~~~~~~~~
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 3, '(this: (this: null, ...args: number[]) => string, thisArg: null, args: number[]): string', gave the following error.
!!! error TS2769: Argument of type 'ArrayLike<string>' is not assignable to parameter of type 'number[]'.
!!! error TS2769: Type 'ArrayLike<string>' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.
!!! error TS2769: Overload 2 of 3, '(this: (this: null, ...args: number[]) => string, thisArg: null, args: ArrayLike<number>): string', gave the following error.
!!! error TS2769: Argument of type 'ArrayLike<string>' is not assignable to parameter of type 'ArrayLike<number>'.
!!! error TS2769: Type 'string' is not assignable to type 'number'.

53 changes: 53 additions & 0 deletions tests/baselines/reference/applyArrayLike.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//// [tests/cases/conformance/functions/applyArrayLike.ts] ////

//// [applyArrayLike.ts]
// Repro for #61835: Function.prototype.apply accepts an array-like second argument
// per the ECMAScript spec (CreateListFromArrayLike), so CallableFunction.apply must
// accept ArrayLike<T>, not just T[]. These cases work at runtime but previously errored.

// --- Array-likes that should now be accepted ---

declare const u8: Uint8Array;
const s1: string = String.fromCharCode.apply(null, u8); // Ok (Uint8Array is ArrayLike<number>)

const forwardArguments: (...args: unknown[]) => void = function () {
console.log.apply(null, arguments); // Ok (IArguments is array-like)
};
forwardArguments(1, 2, 3);

declare function variadic(...xs: number[]): void;
declare const arrayLikeNumbers: ArrayLike<number>;
variadic.apply(undefined, arrayLikeNumbers); // Ok

// A plain array/tuple still works via the arity-checked overload.
declare function foo(a: number, b: string): string;
const a00 = foo.apply(undefined, [10, "hello"]); // Ok

// --- Regression guards: strictBindCallApply arity/element checks must be preserved ---

const a01 = foo.apply(undefined, [10]); // Error: too few elements
const a02 = foo.apply(undefined, [10, 20]); // Error: wrong element type
const a03 = foo.apply(undefined, [10, "hello", 30]); // Error: too many elements

// An array-like of the wrong element type must still be rejected.
declare const arrayLikeStrings: ArrayLike<string>;
const bad = String.fromCharCode.apply(null, arrayLikeStrings); // Error: string is not number


//// [applyArrayLike.js]
"use strict";
// Repro for #61835: Function.prototype.apply accepts an array-like second argument
// per the ECMAScript spec (CreateListFromArrayLike), so CallableFunction.apply must
// accept ArrayLike<T>, not just T[]. These cases work at runtime but previously errored.
const s1 = String.fromCharCode.apply(null, u8); // Ok (Uint8Array is ArrayLike<number>)
const forwardArguments = function () {
console.log.apply(null, arguments); // Ok (IArguments is array-like)
};
forwardArguments(1, 2, 3);
variadic.apply(undefined, arrayLikeNumbers); // Ok
const a00 = foo.apply(undefined, [10, "hello"]); // Ok
// --- Regression guards: strictBindCallApply arity/element checks must be preserved ---
const a01 = foo.apply(undefined, [10]); // Error: too few elements
const a02 = foo.apply(undefined, [10, 20]); // Error: wrong element type
const a03 = foo.apply(undefined, [10, "hello", 30]); // Error: too many elements
const bad = String.fromCharCode.apply(null, arrayLikeStrings); // Error: string is not number
103 changes: 103 additions & 0 deletions tests/baselines/reference/applyArrayLike.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//// [tests/cases/conformance/functions/applyArrayLike.ts] ////

=== applyArrayLike.ts ===
// Repro for #61835: Function.prototype.apply accepts an array-like second argument
// per the ECMAScript spec (CreateListFromArrayLike), so CallableFunction.apply must
// accept ArrayLike<T>, not just T[]. These cases work at runtime but previously errored.

// --- Array-likes that should now be accepted ---

declare const u8: Uint8Array;
>u8 : Symbol(u8, Decl(applyArrayLike.ts, 6, 13))
>Uint8Array : Symbol(Uint8Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))

const s1: string = String.fromCharCode.apply(null, u8); // Ok (Uint8Array is ArrayLike<number>)
>s1 : Symbol(s1, Decl(applyArrayLike.ts, 7, 5))
>String.fromCharCode.apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>String.fromCharCode : Symbol(StringConstructor.fromCharCode, Decl(lib.es5.d.ts, --, --))
>String : Symbol(String, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --) ... and 1 more)
>fromCharCode : Symbol(StringConstructor.fromCharCode, Decl(lib.es5.d.ts, --, --))
>apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>u8 : Symbol(u8, Decl(applyArrayLike.ts, 6, 13))

const forwardArguments: (...args: unknown[]) => void = function () {
>forwardArguments : Symbol(forwardArguments, Decl(applyArrayLike.ts, 9, 5))
>args : Symbol(args, Decl(applyArrayLike.ts, 9, 25))

console.log.apply(null, arguments); // Ok (IArguments is array-like)
>console.log.apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>arguments : Symbol(arguments)

};
forwardArguments(1, 2, 3);
>forwardArguments : Symbol(forwardArguments, Decl(applyArrayLike.ts, 9, 5))

declare function variadic(...xs: number[]): void;
>variadic : Symbol(variadic, Decl(applyArrayLike.ts, 12, 26))
>xs : Symbol(xs, Decl(applyArrayLike.ts, 14, 26))

declare const arrayLikeNumbers: ArrayLike<number>;
>arrayLikeNumbers : Symbol(arrayLikeNumbers, Decl(applyArrayLike.ts, 15, 13))
>ArrayLike : Symbol(ArrayLike, Decl(lib.es5.d.ts, --, --))

variadic.apply(undefined, arrayLikeNumbers); // Ok
>variadic.apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>variadic : Symbol(variadic, Decl(applyArrayLike.ts, 12, 26))
>apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>undefined : Symbol(undefined)
>arrayLikeNumbers : Symbol(arrayLikeNumbers, Decl(applyArrayLike.ts, 15, 13))

// A plain array/tuple still works via the arity-checked overload.
declare function foo(a: number, b: string): string;
>foo : Symbol(foo, Decl(applyArrayLike.ts, 16, 44))
>a : Symbol(a, Decl(applyArrayLike.ts, 19, 21))
>b : Symbol(b, Decl(applyArrayLike.ts, 19, 31))

const a00 = foo.apply(undefined, [10, "hello"]); // Ok
>a00 : Symbol(a00, Decl(applyArrayLike.ts, 20, 5))
>foo.apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>foo : Symbol(foo, Decl(applyArrayLike.ts, 16, 44))
>apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>undefined : Symbol(undefined)

// --- Regression guards: strictBindCallApply arity/element checks must be preserved ---

const a01 = foo.apply(undefined, [10]); // Error: too few elements
>a01 : Symbol(a01, Decl(applyArrayLike.ts, 24, 5))
>foo.apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>foo : Symbol(foo, Decl(applyArrayLike.ts, 16, 44))
>apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>undefined : Symbol(undefined)

const a02 = foo.apply(undefined, [10, 20]); // Error: wrong element type
>a02 : Symbol(a02, Decl(applyArrayLike.ts, 25, 5))
>foo.apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>foo : Symbol(foo, Decl(applyArrayLike.ts, 16, 44))
>apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>undefined : Symbol(undefined)

const a03 = foo.apply(undefined, [10, "hello", 30]); // Error: too many elements
>a03 : Symbol(a03, Decl(applyArrayLike.ts, 26, 5))
>foo.apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>foo : Symbol(foo, Decl(applyArrayLike.ts, 16, 44))
>apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>undefined : Symbol(undefined)

// An array-like of the wrong element type must still be rejected.
declare const arrayLikeStrings: ArrayLike<string>;
>arrayLikeStrings : Symbol(arrayLikeStrings, Decl(applyArrayLike.ts, 29, 13))
>ArrayLike : Symbol(ArrayLike, Decl(lib.es5.d.ts, --, --))

const bad = String.fromCharCode.apply(null, arrayLikeStrings); // Error: string is not number
>bad : Symbol(bad, Decl(applyArrayLike.ts, 30, 5))
>String.fromCharCode.apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>String.fromCharCode : Symbol(StringConstructor.fromCharCode, Decl(lib.es5.d.ts, --, --))
>String : Symbol(String, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --) ... and 1 more)
>fromCharCode : Symbol(StringConstructor.fromCharCode, Decl(lib.es5.d.ts, --, --))
>apply : Symbol(CallableFunction.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>arrayLikeStrings : Symbol(arrayLikeStrings, Decl(applyArrayLike.ts, 29, 13))

Loading