GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Strict bind, call, and apply methods on functions by ahejlsberg · Pull Request #27028 · microsoft/TypeScript

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented Sep 11, 2018 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

This PR introduces a new --strictBindCallApply compiler option (in the --strict family of options) with which the bind, call, and apply methods on function objects are strongly typed and strictly checked. Since the stricter checks may uncover previously unreported errors, this is a breaking change in --strict mode.

The PR includes two new types, CallableFunction and NewableFunction, in lib.d.ts. These type contain generic method declarations for bind, call, and apply for regular functions and constructor functions, respectively. The declarations use generic rest parameters (see #24897) to capture and reflect parameter lists in a strongly typed manner. In --strictBindCallApply mode these declarations are used in place of the (very permissive) declarations provided by type Function.

interface CallableFunction extends Function {
    /**
      * Calls the function with the specified object as the this value and the elements of specified array as the arguments.
      * @param thisArg The object to be used as the this object.
      * @param args An array of argument values to be passed to the function.
      */
    apply<T, R>(this: (this: T) => R, thisArg: T): R;
    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 specified rest arguments as the arguments.
      * @param thisArg The object to be used as the this object.
      * @param args Argument values to be passed to the function.
      */
    call<T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A): R;

    /**
      * For a given function, creates a bound function that has the same body as the original function.
      * The this object of the bound function is associated with the specified object, and has the specified initial parameters.
      * @param thisArg The object to be used as the this object.
      * @param args Arguments to bind to the parameters of the function.
      */
    bind<T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T): (...args: A) => R;
    bind<T, A0, A extends any[], R>(this: (this: T, arg0: A0, ...args: A) => R, thisArg: T, arg0: A0): (...args: A) => R;
    bind<T, A0, A1, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1): (...args: A) => R;
    bind<T, A0, A1, A2, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1, arg2: A2): (...args: A) => R;
    bind<T, A0, A1, A2, A3, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3): (...args: A) => R;
    bind<T, AX, R>(this: (this: T, ...args: AX[]) => R, thisArg: T, ...args: AX[]): (...args: AX[]) => R;
}

interface NewableFunction extends Function {
    /**
      * Calls the function with the specified object as the this value and the elements of specified array as the arguments.
      * @param thisArg The object to be used as the this object.
      * @param args An array of argument values to be passed to the function.
      */
    apply<T>(this: new () => T, thisArg: T): void;
    apply<T, A extends any[]>(this: new (...args: A) => T, thisArg: T, args: A): void;

    /**
      * 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.
      * @param args Argument values to be passed to the function.
      */
    call<T, A extends any[]>(this: new (...args: A) => T, thisArg: T, ...args: A): void;

    /**
      * For a given function, creates a bound function that has the same body as the original function.
      * The this object of the bound function is associated with the specified object, and has the specified initial parameters.
      * @param thisArg The object to be used as the this object.
      * @param args Arguments to bind to the parameters of the function.
      */
    bind<A extends any[], R>(this: new (...args: A) => R, thisArg: any): new (...args: A) => R;
    bind<A0, A extends any[], R>(this: new (arg0: A0, ...args: A) => R, thisArg: any, arg0: A0): new (...args: A) => R;
    bind<A0, A1, A extends any[], R>(this: new (arg0: A0, arg1: A1, ...args: A) => R, thisArg: any, arg0: A0, arg1: A1): new (...args: A) => R;
    bind<A0, A1, A2, A extends any[], R>(this: new (arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, thisArg: any, arg0: A0, arg1: A1, arg2: A2): new (...args: A) => R;
    bind<A0, A1, A2, A3, A extends any[], R>(this: new (arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, thisArg: any, arg0: A0, arg1: A1, arg2: A2, arg3: A3): new (...args: A) => R;
    bind<AX, R>(this: new (...args: AX[]) => R, thisArg: any, ...args: AX[]): new (...args: AX[]) => R;
}

Note that the overloads of bind include up to four bound arguments beyond the this argument. (In the real world code we inspected in researching this PR, practically all uses of bind supplied only the this argument, and a few cases supplied one regular argument. No cases with more arguments were observed.)

Some examples:

// Compile with --strictBindCallApply

declare function foo(a: number, b: string): string;

let f00 = foo.bind(undefined);  // (a: number, b: string) => string
let f01 = foo.bind(undefined, 10);  // (b: string) => string
let f02 = foo.bind(undefined, 10, "hello");  // () => string
let f03 = foo.bind(undefined, 10, 20);  // Error

let c00 = foo.call(undefined, 10, "hello");  // string
let c01 = foo.call(undefined, 10);  // Error
let c02 = foo.call(undefined, 10, 20);  // Error
let c03 = foo.call(undefined, 10, "hello", 30);  // Error

let a00 = foo.apply(undefined, [10, "hello"]);  // string
let a01 = foo.apply(undefined, [10]);  // Error
let a02 = foo.apply(undefined, [10, 20]);  // Error
let a03 = foo.apply(undefined, [10, "hello", 30]);  // Error

class C {
    constructor(a: number, b: string) {}
    foo(this: this, a: number, b: string): string { return "" }
}

declare let c: C;
declare let obj: {};

let f10 = c.foo.bind(c);  // (a: number, b: string) => string
let f11 = c.foo.bind(c, 10);  // (b: string) => string
let f12 = c.foo.bind(c, 10, "hello");  // () => string
let f13 = c.foo.bind(c, 10, 20);  // Error
let f14 = c.foo.bind(undefined);  // Error

let c10 = c.foo.call(c, 10, "hello");  // string
let c11 = c.foo.call(c, 10);  // Error
let c12 = c.foo.call(c, 10, 20);  // Error
let c13 = c.foo.call(c, 10, "hello", 30);  // Error
let c14 = c.foo.call(undefined, 10, "hello");  // Error

let a10 = c.foo.apply(c, [10, "hello"]);  // string
let a11 = c.foo.apply(c, [10]);  // Error
let a12 = c.foo.apply(c, [10, 20]);  // Error
let a13 = c.foo.apply(c, [10, "hello", 30]);  // Error
let a14 = c.foo.apply(undefined, [10, "hello"]);  // Error

let f20 = C.bind(undefined);  // new (a: number, b: string) => C
let f21 = C.bind(undefined, 10);  // new (b: string) => C
let f22 = C.bind(undefined, 10, "hello");  // new () => C
let f23 = C.bind(undefined, 10, 20);  // Error

C.call(c, 10, "hello");  // void
C.call(c, 10);  // Error
C.call(c, 10, 20);  // Error
C.call(c, 10, "hello", 30);  // Error

C.apply(c, [10, "hello"]);  // void
C.apply(c, [10]);  // Error
C.apply(c, [10, 20]);  // Error
C.apply(c, [10, "hello", 30]);  // Error

Fixes #212. (Hey, just a short four years later!)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

styfle, octref, larshp, sami-badawi, olaf89, lulzzz, dsherret, pavloko, WilliamChelman, NeoReyad, and 223 more reacted with thumbs up emoji alessandro-s1mbi0se, hero3616, j-oliveras, Bnaya, forabi, Ebuall, bermanboris, cevek, michaeljota, janriemer, and 5 more reacted with laugh emoji alexrock, felixfbecker, octref, akosyakov, sami-badawi, mohsen1, AlexStacker, dsherret, pavloko, gmfun, and 93 more reacted with hooray emoji Hotell, pavloko, karol-majewski, yuit, j-oliveras, krzkaczor, timocov, nevir, poteto, piotrwitek, and 74 more reacted with heart emoji
All reactions
  • 👍 233 reactions
  • 😄 15 reactions
  • 🎉 103 reactions
  • ❤️ 84 reactions
Anders Hejlsberg (ahejlsberg) added 12 commits September 10, 2018 11:25
@ahejlsberg
Better scheme for choosing between co- and contra-variant inferences
46bd405
@ahejlsberg
Accept new baselines
df83784
@ahejlsberg
New --strictBindCallApply flag in compiler
22a384d
@ahejlsberg
New CallableFunction and NewableFunction types in es5.d.ts
55b6513
@ahejlsberg
Accept new baselines
a85b896
@ahejlsberg
Add CallableFunction/NewableFunction to virtual file system
b687d90
@ahejlsberg
Accept new baselines
1e3625c
@ahejlsberg
Update declarations
a5dece3
@ahejlsberg
Update getAugmentedPropertiesOfType
d069da2
@ahejlsberg
Add tests
e9679f0
@ahejlsberg
Accept new baselines
8a41f5f
@ahejlsberg
Minor fix
91123fc
@ahejlsberg
Anders Hejlsberg (ahejlsberg) requested a review from Daniel Rosenwasser (DanielRosenwasser) September 11, 2018 17:05
Anders Hejlsberg (ahejlsberg) added 2 commits September 11, 2018 10:48
@ahejlsberg
Merge branch 'master' into typedBindCallApply
b67a261
# Conflicts:
#	src/compiler/diagnosticMessages.json
@ahejlsberg
Accept new baselines
0226b66
@ahejlsberg
Anders Hejlsberg (ahejlsberg) requested review from Ryan Cavanaugh (RyanCavanaugh), Nathan Shively-Sanders (sandersn) and Wesley Wigham (weswigham) September 11, 2018 17:56
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Sep 11, 2018
bind(), call(), and apply() are untyped #212
Closed
weswigham
Wesley Wigham (weswigham) reviewed Sep 11, 2018
View reviewed changes
Comment thread src/compiler/checker.ts
if (resolved === anyFunctionType || resolved.callSignatures.length || resolved.constructSignatures.length) {
const symbol = getPropertyOfObjectType(globalFunctionType, name);
const functionType = resolved === anyFunctionType ? globalFunctionType :
resolved.callSignatures.length ? globalCallableFunctionType :

@weswigham Wesley Wigham (weswigham) Sep 11, 2018

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we prefer callable to newable if a given type is both?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Sep 11, 2018

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I have a convincing argument for either preference. All I know is that it is very rare to have both and the two sets of signatures presumably align such that calling without new just causes the function to allocate an instance (as is the case with Array<T>, for example). I also know that having a combined list of overloads causes error reporting to be poor for one side because we always use the last arity-matching signature as the error exemplar.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@weswigham Wesley Wigham (weswigham) Sep 12, 2018

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not use both sets of overloads for resolution, and if both fail, simply prefer one set or the other just when reporting errors?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Sep 12, 2018

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, not easily. We assume that all overloads are in a single list of signatures, so we'd somehow have to merge them or declare a third type (CallableNewableFunction) in lib.d.ts that has the combined sets of overloads.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@ChiriVulpes

Chiri Vulpes (ChiriVulpes) commented Sep 11, 2018 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Correct me if I'm wrong, but won't the bind method overloads fail on functions with variable arguments? I use this kind of thing in my codebases... (not often, but they do exist)

function foo(...numbers: number[]) {}
const bar = [1, 2, 3];
foo.bind(undefined, ...bar);
KaloyanDimitrov, genaby, bcherny, and cshaa reacted with thumbs up emoji
All reactions
  • 👍 4 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Sep 11, 2018

Copy link
Copy Markdown
Member Author

Correct me if I'm wrong, but won't the bind method overloads fail on functions with variable arguments?

Yes, your example fails when bar is an array (but succeeds if bar is a tuple with four or less number elements). I think you're right, we need an extra overload to handle binding of functions with rest parameter arrays. That is:

interface CallableFunction extends Function {
    // ...
    bind<T, AX, R>(this: (this: T, ...args: AX[]) => R, thisArg: T, ...args: AX[]): (...args: AX[]) => R;
}

interface NewableFunction extends Function {
    // ...
    bind<AX, R>(this: new (...args: AX[]) => R, thisArg: any, ...args: AX[]): new (...args: AX[]) => R;
}

Should be pretty easy to add.

connorjclark, haltcase, rceballosdf, tion8888, cbll, IllusionMH, SimonKocurek, matijagrcic, felixfbecker, hero3616, and 2 more reacted with thumbs up emoji
All reactions
  • 👍 12 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Jessidhia

Jessica Franco (Jessidhia) commented Sep 12, 2018

Copy link
Copy Markdown

The second argument to apply is not actually an array but an ArrayLike, though I'm not sure how important that is on real world code; almost always when it's not an array it's just an arguments object.

ljharb, styfle, sean-vieira, troy351, and AprilArcus reacted with thumbs up emoji
All reactions
  • 👍 5 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ljharb

Jordan Harband (ljharb) commented Sep 12, 2018

Copy link
Copy Markdown
Contributor

It's very important that it be an ArrayLike, since people .apply with NodeLists, arrays, and arguments objects.

styfle, sean-vieira, and c69 reacted with thumbs up emoji
All reactions
  • 👍 3 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@felixfbecker

Felix Becker (felixfbecker) commented Sep 12, 2018

Copy link
Copy Markdown
Contributor

Some part of me wishes this was not behind a flag. What’s an example of a usage of apply/bind that is correct but would break with this?

If the two interfaces are only “injected” if the flag is on, how can libraries reference them without making an assumption on the consumer’s flag seeting?

alessandro-s1mbi0se, janmeier, aleclarson, poseidonCore, and sindresorhus reacted with thumbs up emoji
All reactions
  • 👍 5 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

sandersn
Nathan Shively-Sanders (sandersn) reviewed Sep 12, 2018
View reviewed changes

@sandersn Nathan Shively-Sanders (sandersn) left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One initial comment, plus can you elaborate on the breaks that result from having this on everywhere as Felix Becker (@felixfbecker) requested? Or was that discussed during the last design meeting when I was out?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/compiler/checker.ts
}
if (resolved === anyFunctionType || resolved.callSignatures.length || resolved.constructSignatures.length) {
const symbol = getPropertyOfObjectType(globalFunctionType, name);
const functionType = resolved === anyFunctionType ? globalFunctionType :

@sandersn Nathan Shively-Sanders (sandersn) Sep 12, 2018

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be put into a function and anyFunctionType’s declaration needs a comment saying “Use this function instead”.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
sandersn
Nathan Shively-Sanders (sandersn) requested changes Sep 12, 2018
View reviewed changes

@sandersn Nathan Shively-Sanders (sandersn) left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the variance change needed?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/compiler/checker.ts
if (!inferredType) {
const signature = context.signature;
if (signature) {
if (inference.contraCandidates && (!inference.candidates || inference.candidates.length === 1 && inference.candidates[0].flags & TypeFlags.Never)) {

@sandersn Nathan Shively-Sanders (sandersn) Sep 12, 2018

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this change needed?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Sep 12, 2018

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variance change is there to ensure we infer the most specific type possible when we have both co- and contra-variant candidates. Furthermore, knowing that an error will result when the co-variant inference is not a subtype of the contra-variant inference, we now prefer the contra-variant inference because it is likely to have come from an explicit type annotation on a function. This improves our error reporting.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

matijagrcic reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread src/compiler/checker.ts
globalArrayType = getGlobalType("Array" as __String, /*arity*/ 1, /*reportErrors*/ true);
globalObjectType = getGlobalType("Object" as __String, /*arity*/ 0, /*reportErrors*/ true);
globalFunctionType = getGlobalType("Function" as __String, /*arity*/ 0, /*reportErrors*/ true);
globalCallableFunctionType = strictBindCallApply && getGlobalType("CallableFunction" as __String, /*arity*/ 0, /*reportErrors*/ true) || globalFunctionType;

@sandersn Nathan Shively-Sanders (sandersn) Sep 12, 2018

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment here is also a good idea.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread tests/baselines/reference/genericRestArityStrict.errors.txt
handler: (...args: TS) => void,
...args: TS): void;

call((x: number, y: number) => x + y);

@sandersn Nathan Shively-Sanders (sandersn) Sep 12, 2018

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this improved error because of the variance inference change?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Sep 12, 2018

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

sandersn reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread tests/baselines/reference/booleanLiteralsContextuallyTypedFromUnion.errors.txt
error TS2318: Cannot find global type 'NewableFunction'.


!!! error TS2318: Cannot find global type 'CallableFunction'.

@sandersn Nathan Shively-Sanders (sandersn) Sep 12, 2018

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably shouldn’t get an error here.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Sep 12, 2018

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't want to update the private lib.d.ts used by the tests as I suspect that'll cause some really noisy baseline changes.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

sandersn and weswigham reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

@sandersn Nathan Shively-Sanders (sandersn) Sep 12, 2018

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, just wanted to make sure this error wouldn't show up in some public-facing scenario.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Sep 12, 2018

Copy link
Copy Markdown
Member Author

The second argument to apply is not actually an array but an ArrayLike, though I'm not sure how important that is on real world code; almost always when it's not an array it's just an arguments object.

There is currently no type-safe way to use an ArrayLike<T> as the argument list in apply. I think this is fine since, as you point out, the typical use case is the arguments object which is inherently untyped anyway. To use an array-like with apply in --strictBindCallApply mode you need to cast the function object to type Function or cast the argument array to any:

(myFunction as Function).apply(undefined, arguments);
myFunction.apply(undefined, arguments as any);

Note that you can still call or apply methods of Array<T> with an ArrayLike<T> as the thisArg:

function printArguments() {
    Array.prototype.forEach.call(arguments, (item: any) => console.log(item));
}

This is permitted because we only strictly check the thisArg if the function on which you're invoking call or apply has an explicit this parameter. We might someday also tighten this up with a --strictThis flag, although there are several non-trivial issues with doing that. We're tracking this in #7968.

c69 reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Sep 12, 2018

Copy link
Copy Markdown
Member Author

Felix Becker (@felixfbecker) There is a lot of code out there today that uses apply with the arguments object to intercept functions. A substantial portion of our real world code suites break with this type of error when the stricter checking is enabled. It's technically correct to report the issue (the affected code is unsafe and an explicit cast is merited), but without a compiler switch it becomes a gating issue for adopting the latest compiler. The main point of --strict mode is to signal that you're ok with such breakage. But conversely, when you're not --strict we take it as a signal to minimize breakage.

ulrichb, drewnoakes, and matijagrcic reacted with thumbs up emoji
All reactions
  • 👍 3 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ljharb

Jordan Harband (ljharb) commented Sep 12, 2018

Copy link
Copy Markdown
Contributor

Anders Hejlsberg (@ahejlsberg) { length: number }?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@SlurpTheo

SlurpTheo commented Oct 9, 2018

Copy link
Copy Markdown

(Extra points for the added subtlety of spreading a string into individual characters!)

Ha/yes -- I've never wanted that to have happened in my code when it has; be great to have some sort of warning about it! 😸

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mattmccutchen

Matt McCutchen (mattmccutchen) commented Oct 12, 2018 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

It looks like the new feature doesn't work well for generic or overloaded functions. Silly example:

function foo<T>(name: string, arg: T): T {
    console.log(name);
    return arg;
}

// Type of `fooResult` is `{}`. :(
let fooResult = foo.bind(undefined, "Matt")("TypeScript");

function bar(name: string, arg: number): number;
function bar(name: string, arg: string): string;
function bar(name: string, arg: string | number) {
    console.log(name);
    return (typeof arg === "number") ? arg + 1 : arg + "1";
}

// Error: Argument of type '5' is not assignable to parameter of type 'string'.
let barResult = bar.bind(undefined, "Matt")(5);

It would be good to document this as a limitation in the "What's new in TypeScript" entry.

dragomirtitian, benjaminjackman, nicojs, SlurpTheo, balupton, iFwu, ekashida, weakish, ferk6a, rmariuzzo, and 3 more reacted with thumbs up emoji
All reactions
  • 👍 13 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ghost ghost mentioned this pull request Oct 18, 2018
Type inference regression with overloads #27972
Closed
@NN---

NN (NN---) commented Oct 21, 2018

Copy link
Copy Markdown

Is there a reason to not use 'unknown' instead of 'any' ?

    bind<T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T): (...args: A) => R;
szagi3891 reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@szagi3891

Grzegorz Szeliga (szagi3891) commented Nov 8, 2018

Copy link
Copy Markdown

Anders Hejlsberg (@ahejlsberg)

Is it possible to extend this functionality so that the compiler can detect errors of this type?

class A {
    constructor(
        readonly name: string
    ) {}

    show() {
        console.info(`Name: ${this.name}`);
    }
}

const a = new A('aaa');
const showFn = a.show;

showFn();

Launching this code generates an error:

TypeError: this is undefined
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mattmccutchen

Matt McCutchen (mattmccutchen) commented Nov 8, 2018 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Grzegorz Szeliga (@szagi3891) Your example is covered by #7968; AFAICT it has nothing to do with the current issue because you aren't using bind, call, or apply.

szagi3891, jesseschalken, and SlurpTheo reacted with thumbs up emoji
All reactions
  • 👍 3 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) mentioned this pull request Nov 16, 2018
lib.d.ts Function.bind with this #28571
Closed
5 tasks
This was referenced Nov 17, 2018
Incorrect overload resolution in 3.2.0-rc #28567
Closed
[3.2.0-rc] .bind with overloads causes compile error in strict mode (strictBindCallApply) #28582
Closed
@sandersn Nathan Shively-Sanders (sandersn) mentioned this pull request Nov 20, 2018
TS 3.2 updates DefinitelyTyped/DefinitelyTyped#30700
Merged
@DrMiaow

James McParlane (DrMiaow) commented Dec 3, 2018 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

What is the solution in the case of using generic or overloaded functions?

Is there going to be an updated version that will handle generic or overloaded functions? I'm trying to decide if I should pin to the previous working version of Typescript until you release a working version.

Update:

      const factoryFunction = event.bind.apply(event, args)
      return new factoryFunction()

=>

      const factoryFunction = (event as any).bind.apply(event, args)
      return new factoryFunction()

Bypasses this for now, but ouch.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Jessidhia

Jessica Franco (Jessidhia) commented Dec 3, 2018

Copy link
Copy Markdown

It's no worse than it was before, but yes, you're going to need a roundtrip through as any. Or maybe better, as Function.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@balupton

Benjamin Lupton (balupton) commented Dec 5, 2018 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

It looks like the new feature doesn't work well for generic or overloaded functions.
#27028 (comment)

Is that also why the following doesn't work?

export function binder<
	Method extends (this: Context, ...args: Args) => Result,
	Context,
	Args extends any[],
	Result
>(method: Method, context: Context, ...args: Args) {
	return method.bind(context)
}

const context = { local: 'string value' }

function fn(this: typeof context, arg: string) {}

binder(fn, context) // [ts] Argument of type '(this: { local: string; }, arg: string) => void' is not assignable to parameter of type '(this: { local: string; }) => {}'. [2345]

It would be nice if the error code 2345 was switched to something more specific about this.

I ask because I am trying to figure out how to model the below in TypeScript (yet the above seems a hurdle in its accomplishment):

function binder (method, context, …args) {
	const unbounded = method.unbounded || method
	const bounded = method.bind(context, ...args)
	Object.defineProperty(bounded, 'unbounded', {
		value: unbounded,
		enumerable: false,
		configurable: false,
		writable: false
	})
	return bounded
}

The latter seems impossible to model in TypeScript correctly.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ulrichb ulrichb mentioned this pull request Dec 7, 2018
False positive assignment error with generics when using --strictBindCallApply #28900
Closed
@glen-84

Glen (glen-84) commented Dec 18, 2018

Copy link
Copy Markdown

James McParlane (@DrMiaow) Another option is @ts-ignore, but it's not ideal without #19139.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@wKich Dmitriy Lazarev (wKich) mentioned this pull request Jan 9, 2019
Function overload breaks generics #29312
Closed
@MartinJohns Martin Johns (MartinJohns) mentioned this pull request Jul 6, 2019
the typing the second parameter of push.apply should be readonly T[], not T[] #32276
Closed
@orta Orta Therox (orta) mentioned this pull request Jul 10, 2019
Playground missing strictBindCallApply microsoft/TypeScript-Website#12
Closed
@andreasgerstmayr Andreas Gerstmayr (andreasgerstmayr) mentioned this pull request Aug 9, 2019
typing performancecopilot/grafana-pcp#6
Closed
4 of 6 tasks
@brainkim Brian Kim (brainkim) mentioned this pull request Oct 4, 2019
bind, call and apply do not work on unions of function types with different return types. #33815
Open
@BlackGlory BlackGlory mentioned this pull request Oct 9, 2019
Strict Reflect.apply Reflect.construct and Function.prototype.apply/call/bind #33889
Open
5 tasks done
@GeroCoded Luis Gerónimo (GeroCoded) mentioned this pull request Nov 14, 2020
Go to Definition refers to VSCode node_modules instead of project's node_modules microsoft/vscode#110631
Closed
@rajmondx Rajmond Xhaferi (rajmondx) mentioned this pull request May 30, 2021
NewableFunction not working as intended #44337
Closed
@MartinJohns Martin Johns (MartinJohns) mentioned this pull request Jun 10, 2021
First Argument in Function.prototype.call should be optional #44530
Closed
@thscott thscott mentioned this pull request Feb 14, 2022
Return type of partially applied bound generic function #47899
Closed
Bung (bung87) added a commit to bung87/vscode-rails that referenced this pull request Apr 3, 2022
@bung87
strictBindCallApply microsoft/TypeScript#27028
678a3ae
@jcalz Joe Calzaretta (jcalz) mentioned this pull request Jun 21, 2023
Generics are lost during Function.prototype.bind() and Function.prototype.call() #54707
Open
5 tasks done
@Andarist Mateusz Burzyński (Andarist) mentioned this pull request Mar 22, 2024
Improve logic that chooses co- vs. contra-variant inferences #57909
Merged
@olivermrose Oliver Rose (olivermrose) mentioned this pull request Nov 9, 2024
Refine the type signatures of the functions apply, call, and bind. #60461
Closed
@microsoft Microsoft (microsoft) locked as resolved and limited conversation to collaborators Oct 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

@sandersn Nathan Shively-Sanders (sandersn) sandersn requested changes
@weswigham Wesley Wigham (weswigham) weswigham approved these changes
@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) Awaiting requested review from DanielRosenwasser
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) Awaiting requested review from RyanCavanaugh

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

TypeScript 3.2

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

15 participants

@ahejlsberg @ChiriVulpes @Jessidhia @ljharb @felixfbecker @weswigham @RyanCavanaugh @SlurpTheo @mattmccutchen @NN--- @szagi3891 @DrMiaow @balupton @glen-84 @sandersn
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.