Skip to content

Error "The 'this' context of type 'void' is not assignable to method's 'this' ..." when using an intermediate function instead of "!" #23304

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
stephanedr opened this issue Apr 10, 2018 · 6 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@stephanedr
Copy link

Same situation than #23040, but using an intermediate function instead of using "!".

TypeScript Version: 2.9.0-dev.20180410

Search Terms: "'this' context of type 'void' is not assignable"

Code

interface Foo {
    required: ((this: Foo) => void);
    optional: ((this: Foo) => void) | null;
}

function notNull<T>(value: T): NonNullable<T> {
    if (value == null) throw Error("notNull() failed");
    return value as NonNullable<T>;
}

declare const foo: Foo;
foo.required();
foo.optional!();   // Fixed by #23040.
notNull(foo.optional)();  // Error.

Expected behavior:
No Error.

Actual behavior:
The 'this' context of type 'void' is not assignable to method's 'this' of type 'Foo'.

Playground Link: https://www.typescriptlang.org/play/index.html#src=interface%20Foo%20%7B%0D%0A%20%20%20%20required%3A%20((this%3A%20Foo)%20%3D%3E%20void)%3B%0D%0A%20%20%20%20optional%3A%20((this%3A%20Foo)%20%3D%3E%20void)%20%7C%20null%3B%0D%0A%7D%0D%0A%0D%0Afunction%20notNull%3CT%3E(value%3A%20T)%3A%20NonNullable%3CT%3E%20%7B%0D%0A%20%20%20%20if%20(value%20%3D%3D%20null)%20throw%20Error(%22notNull()%20failed%22)%3B%0D%0A%20%20%20%20return%20value%20as%20NonNullable%3CT%3E%3B%0D%0A%7D%0D%0A%0D%0Adeclare%20const%20foo%3A%20Foo%3B%0D%0Afoo.required()%3B%0D%0Afoo.optional!()%3B%20%20%20%2F%2F%20Fixed%20by%20%2323040.%0D%0AnotNull(foo.optional)()%3B%20%20%2F%2F%20Error.%0D%0A

Related Issues: #23040

@ghost
Copy link

ghost commented Apr 10, 2018

That code will actually fail at runtime:

interface Foo {
    required: ((this: Foo) => void);
    optional: ((this: Foo) => void) | null;
}

function notNull<T>(value: T): NonNullable<T> {
    if (value == null) throw Error("notNull() failed");
    return value as NonNullable<T>;
}

const foo: Foo = {
    required() {},
    optional() {
        this.required();
    },
};
(notNull(foo.optional) as any)();  // Error.

Fails with TypeError: Cannot read property 'required' of undefined, because the call notNull(foo.optional)() doesn't have foo as the this receiver.

@mhegazy mhegazy added the Working as Intended The behavior described is the intended behavior; this is not a bug label Apr 10, 2018
@stephanedr
Copy link
Author

Indeed, at JS level, call() has to be called, so ideally generated by TS.

The reason is that ! is a "blind" non-null assertion, whereas I definitely prefer checking that, whenever I assume something, it is really true (notNull() is a debug feature that is removed in release mode).
And if I use call() I lose the benefit of argument checking...
Additionally, if one day conditional compilation is implemented (#3538), we'll face the same kind of issue.

@ghost
Copy link

ghost commented Apr 10, 2018

foo.optional!() gets a runtime check too -- it throws a TypeError if foo.optional was undefined.

@stephanedr
Copy link
Author

@Andy-MS, fully agree, but in fact my notNull() implementation throws a specific AssertionError (as well as other debug functions) that are reported/routed differently than other errors.

@stephanedr
Copy link
Author

To be clearer, AssertionErrors are necessarily programming errors, so directly reported to developers, whereas other errors may be due to user errors.

@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@microsoft microsoft locked and limited conversation to collaborators Jul 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants