Skip to content
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

calling generic function declared at base class with union type cause unexpected compile error #49796

Closed
wenxiao opened this issue Jul 6, 2022 · 3 comments
Labels
Duplicate An existing issue was already created

Comments

@wenxiao
Copy link

wenxiao commented Jul 6, 2022

Bug Report

Calling generic function declared at base class with union type cause unexpected compile error.

πŸ”Ž Search Terms

Union generic, keyof this

πŸ•— Version & Regression Information

TypeScript version: 4.7.4

⏯ Playground Link

Playground link with relevant code

πŸ’» Code


class BaseClass {
    func<K extends keyof this>(k: K) {}
}

class A extends BaseClass {
    a = 1;
    b = 2;
}

class B extends BaseClass {
    b = 3;
    c = 4;
}

function func<T, K extends keyof T>(v: T, k: K) {}

let a = new A();
let ab: A | B = new A() as any;

func(a, 'a');
func(a, 'b');
// func(a, 'c'); // compile error

// func(ab, 'a'); // compile error
func(ab, 'b');

a.func('a');
a.func('b');
// a.func('c'); // compile error

// ab.func('a'); // compile error
ab.func('b'); // compile error NOT EXPECTED

πŸ™ Actual behavior

Compiler Errors

This expression is not callable.
Each member of the union type '((k: K) => void) | ((k: K) => void)' has signatures, but none of those signatures are compatible with each other.

πŸ™‚ Expected behavior

last line should not raise Compiler Errors, (<K extends keyof A>(k: K) => void) | (<K extends keyof B>(k: K) => void) should be union to (<K extends keyof A|B>(k: K) => void) ?

Also notice that global function "func" can work as expected.

@RyanCavanaugh
Copy link
Member

Duplicate #33591, others.

last line should not raise Compiler Errors, ((k: K) => void) | ((k: K) => void) should be union to (<K extends keyof A|B>(k: K) => void) ?

In general this is not a safe reduction step: contravariant uses of K would become unsound. It's very rarely possible to safely merge two arbitrary signatures.

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Jul 6, 2022
@wenxiao
Copy link
Author

wenxiao commented Jul 7, 2022

@RyanCavanaugh I found a workaround here:

class BaseClass {
    func<K extends keyof this>(k: K) {}
}

class A extends BaseClass {
    a = 1;
    b = 2;
}

class B extends BaseClass {
    b = 3;
    c = 4;
}

function func<T, K extends keyof T>(v: T, k: K) {}

function workaroundFunc<T extends BaseClass, K extends keyof T>(v:T, k:K){
    v.func(k)
}

let a = new A();
let ab: A | B = new A() as any;

func(a, 'a');
func(a, 'b');
// func(a, 'c'); // compile error

// func(ab, 'a'); // compile error
func(ab, 'b');

a.func('a');
a.func('b');
// a.func('c'); // compile error

// ab.func('a'); // compile error
// ab.func('b'); // compile error NOT EXPECTED
// workaroundFunc(ab, 'a'); // compile error
workaroundFunc(ab, 'b');

Playground link with relevant code

by using workaroundFunc, I can do what I expected to do. I'm not sure what's the different between workaroundFunc and direct call.

@typescript-bot
Copy link
Collaborator

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants