Skip to content

CalableFunction interface can be assigned to variable with another 'call' method signature #41135

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
bingo347 opened this issue Oct 16, 2020 · 2 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@bingo347
Copy link

TypeScript Version: 4.0.2

Search Terms: CallableFunction, call, call inference

Expected behavior:
Error reporting what type mismatch in call method:

interface Callable<Return, Arguments extends any[], This = unknown> {
    call(thisArg: This, ...args: Arguments): Return;
}
declare function incorrectFunction(a: string, b: number): void;
declare function testFunction(f: Callable<string, [number, boolean], undefined>): void;
testFunction(incorrectFunction);

Actual behavior:
No type check error reporting

Related Issues:
I found only this: #30294
but my example without generic functions

Code

interface Callable<Return, Arguments extends any[], This = unknown> {
    call(thisArg: This, ...args: Arguments): Return;
}

declare function correctFunction(a: number, b: boolean): string;
declare function incorrectFunction(a: string, b: number): void;
declare function testFunction(f: Callable<string, [number, boolean], undefined>): void;

// it is Ok
const f1: Callable<string, [number, boolean], undefined> = correctFunction;
testFunction(correctFunction);

// it is not reporting type mismatch
const f2: Callable<string, [number, boolean], undefined> = incorrectFunction;
const f3: Callable<string, [number, boolean], undefined> = () => 1;
testFunction(incorrectFunction);
testFunction((a: number[], b: number[]) => a.concat(b));
f2.call(void 0, 10, true).toLowerCase(); // runtime error here...

Compiler Options

{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true
  }
}

Playground Link: https://www.typescriptlang.org/play?noUnusedLocals=true&noUnusedParameters=true&esModuleInterop=false&declaration=false&experimentalDecorators=false&emitDecoratorMetadata=false&target=7&jsx=0#code/JYOwLgpgTgZghgYwgAgMJwDYbgIwxAHgCUIwBXKEAGmQEEoBzMgWwnAGdkIAPSEAE05wQATwDaAXRoAVABbBOAXmRkQAaxAB7AO4gAfMgDeAKGRnkCTBgAUYee3oMAXMjkKaAOi9xG7F45Y2MHYAShcSckoAbmMAX2NjfggEbCgUGFUEMGBNEAtNKDSsgDFM7NzrOBcQFhxoGhwXHE1NfGEw5HYwKFAGGKSUn3SynLzQBAKisFKQLNHKly6ekAYG6troDoA3TWB+fuTU4dnyvMgumbmKmBd0LFx8AiXemjEa5jqoBpa2kCkVAQQGCgCD8PTbXb7BIAemhyGAYHhnAA8mpjBMQF1kDAAIy3KwPQjPFavd6fb6tCDCf6qJLAkCggzKCaFZLTEa5GLndkneYsqaXU4hGLGWHwxEKZBaRFpAAOBWyK2QYBEspQzAUzDgYAQsnRuSxMAATPj7ngid0Xsg3hsvshmpTqTRaUCQWDkMpxpM2YLRjEMYaAMym7Dmp6WknWsn1e0-Kl-Z2A+mMj3IawhD0GHFciAXDkgaxe1klfPC4zc30VBZS22SNY1j7QSQZxQGOAeDGWMDWHAhMvGjtWaw7PbIAAMNBxE+VUDIEBCHjAmgAMjpoOh2BB01FkGLZ+BgKwuIUCshZNAIF4PDC4Qikcg5QresYiwL84OsMPIePJ9PunOFyXVdtHXOBN23G98nANJuDgZhZXwZBtARWR8mLGUIHlKBFQYUU4SSZgDW6bVemVWRtTIyVJXldh2GAc1sVPFU1XYBAellMAX3AaB4CQNBvSyAhpAMExzGQVg7E0fhKkYFxpAhPYYniAMJWCAB1Ao1FuASwHDZYGCZIxTHMCTZCk6wAH0fGcBtPgU-gjHiWIoiAA

@RyanCavanaugh
Copy link
Member

strictBindCallApply doesn't kick in here because it's effectively an indirect reference to incorrectFunction.call rather than a direct reference, and call only gets the more specific types during a direct reference. In the indirect form, we fall back to the argumentless version, which is a valid assignment source.

Callable should just be written in terms of a bare function signature, at which point this all works out as expected.

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Oct 16, 2020
@bingo347
Copy link
Author

bingo347 commented Oct 16, 2020

strictBindCallApply doesn't kick in here because it's effectively an indirect reference to incorrectFunction.call rather than a direct reference, and call only gets the more specific types during a direct reference. In the indirect form, we fall back to the argumentless version, which is a valid assignment source.

Callable should just be written in terms of a bare function signature, at which point this all works out as expected.

In my case I wanna get callable object as argument of my function, but just function also ok for me, if it have correct signature.

interface Callable<Return, Arguments extends any[], This = unknown> {
    call(thisArg: This, ...args: Arguments): Return;
}

declare function internalLogic(cb: (x: number) => void): void;
function useCallable(f: Callable<boolean, [number]>): void {
    internalLogic(x => {
        if(f.call(void 0, x)) {
            // do somethink
        }
    })
}

// it is usual usage:
class SomeCallble implements Callable<boolean, [number]> {
    constructor(
        private v: number
    ) {}

    public call(_: unknown, x: number): boolean {
        return x < this.v;
    }
}
useCallable(new SomeCallble(100));

// it also correct in some cases
useCallable((x: number) => x < 100);

// but typescript does not report these types:
useCallable((x: string) => x.length)

https://www.typescriptlang.org/play?noUnusedLocals=true&noUnusedParameters=true&esModuleInterop=false&declaration=false&experimentalDecorators=false&emitDecoratorMetadata=false&target=7&jsx=0#code/JYOwLgpgTgZghgYwgAgMJwDYbgIwxAHgCUIwBXKEAGmQEEoBzMgWwnAGdkIAPSEAE05wQATwDaAXRoAVABbBOAXmRkQAaxAB7AO4gAfMgDeAKGRnkCTBgAUYee3oMAXMjkKaAOi9xG7F45Y2MHYAShcSckoAbmMAX2NjfggEbCgUGFUEMGBNEGRQSEpMABlNBmAEawQcF2tuFxAWHGgQ5EUDADdNYH4w5C6emIyQLJy8snYIdCxcfGsYF2nsPEIcTU18YRoxRuZmqAk9PoH+I1NzAugQErKKuraDE3Nn-Jh5j0ssaxPkAAYabghVpPF6ggD0YOQ-E0yHYmlYdlAanOoPiz1iITiCQh+TA+U4EzImBU7DgDAgTmMKTg7E4AGV4VMrCt8swAA74VgcNBWWardabajIHZNaCHM7PBC5dhgKBkLKaKDWFEvNlQYAdOCQfoNUVQFXA+IqtlkPAVCxWawAfRcqg0OiF9WQu32fTWGwgwgloOQaUieW4yAIyER7A8HRi6KxEyZMxW1hAEG0yAZrCW8YAjL9fkCYsYccA8Zg4RZFWksvk8nDWBbJuxjDGlnzrHVdXsWg9kIHg1mc3mcTgyHiwCI2RB2Ah1Wy8dDx87NHi0mzFcPZOOUCOx34G5Mm-HW7DZaAGK12l2PPgQAw7CEgA

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

2 participants