strictBindCallApply
Type-checks the arguments and result of call, apply and bind instead of returning any.
"strictBindCallApply": boolean // Default: true if strict; false otherwise- Added in TypeScript 3.2
Parameters
| Parameter | Type | Description |
|---|---|---|
$strictBindCallApplyoptional | boolean= true if strict; false otherwise | When true, the built-in call, bind and apply methods are checked against the underlying function's parameters and return type. When false they accept any arguments and return any. |
Errors
Nothing at runtime; it reports error 2345, 'Argument of type X is not assignable to parameter of type Y'.
Changelog
- PHP 3.2Introduced together with the CallableFunction and NewableFunction interfaces in lib.es5.d.ts, which is where the strict overloads live.
Without this flag, call, apply and bind come from Function.prototype, whose signatures accept ...args: any[] and return any. Every argument check and the result type are lost the moment you use them. With the flag on, TypeScript uses the CallableFunction and NewableFunction overloads instead, so the arguments are checked against the real parameter list and the result keeps its type.
What it covers
fn.call(thisArg, ...args)— arguments checked positionallyfn.apply(thisArg, argsArray)— tuple checked against the parameter listfn.bind(thisArg, ...partialArgs)— the returned function keeps the remaining parameters
What trips people up
The thisArg is checked too, but only when the function declares a this parameter — which requires noImplicitThis to be useful. Rest and optional parameters weaken the check: apply against (...args: number[]) can only verify the element type, not the count. And bind on an overloaded function resolves against the last overload, which regularly surprises people who expected the one they had in mind. Arrow functions and closures avoid all of this and are usually the better choice.
Example
// "strictBindCallApply": true
function fn(x: string) {
return parseInt(x, 10);
}
const n1 = fn.call(undefined, "10"); // n1: number
// fn.call(undefined, false) -> error 2345: boolean is not assignable to string
const bound = fn.bind(undefined, "42"); // bound: () => number
const applied = fn.apply(undefined, ["7"]);
console.log(n1); // 10
console.log(bound()); // 42
console.log(applied); // 7When to use it
- Partially applying a function with bind and keeping the remaining parameters typed
- Spreading a tuple into apply and having the arity checked
- Keeping the return type of a call-forwarded helper instead of getting any
Frequently asked questions
Why does fn.call return any in my project?
Because strictBindCallApply is off, so call, bind and apply resolve to the loose Function.prototype signatures that take any[] and return any. Enable the flag — or strict — to get the checked CallableFunction overloads.
Does strictBindCallApply check the thisArg?
Only when the function declares an explicit this parameter, such as function fn(this: Widget, x: string). Without one, any thisArg is accepted, which is why this flag pairs with noImplicitThis.
Why is bind on an overloaded function typed wrong?
bind resolves against the last overload signature rather than the one matching your arguments. Wrap the call in an arrow function instead, which resolves overloads normally and keeps the intended types.
More examples
What you lose with the flag off
Prints NaN, number, true. parseInt(false) yields NaN, and with the flag off nothing warns you: the result was typed any, so even NaN.toFixed-style follow-on errors go unreported.
function fn(x: string) {
return parseInt(x, 10);
}
// strictBindCallApply: false
// const n = fn.call(undefined, false); // no error, n: any
// strictBindCallApply: true
// error 2345 on `false`, and n is number
const n = fn.call(undefined, false as unknown as string);
console.log(n); // NaN
console.log(typeof n); // number
console.log(Number.isNaN(n)); // truebind keeps the remaining parameters
Both logs print "size: 12MB". The value of the flag is in the commented lines: bound arity and parameter types are still enforced, where without it mb would be typed as any and both mistakes would compile.
function format(prefix: string, value: number, unit: string) {
return prefix + ": " + value + unit;
}
const mb = format.bind(null, "size");
// mb: (value: number, unit: string) => string
console.log(mb(12, "MB")); // size: 12MB
// mb("12", "MB") -> error 2345: string is not assignable to number
// mb(12) -> error 2554: expected 2 arguments, but got 1
const all = format.bind(null, "size", 12, "MB");
console.log(all()); // size: 12MB
Discussion