Why is the variable unknown?

Viewed 61

Please look at this example. I wonder why the p-parameter is unknown in the function test2. I expected p to be known as a number.

interface MyInterface<A,B>{}

class MyClass<A,B> implements MyInterface<A,B> {}

function test1<A,B>(p:MyClass<A,B>, callback:(p:B)=>void){}

function test2<A,B>(p:MyInterface<A,B>, callback:(p:B)=>void){}

const myInstance = new MyClass<string,number>()

test1(myInstance, (p)=>{    
    // Here p is a number
}) 

test2(myInstance, (p)=>{
    // Why is p unknown?
}) 
1 Answers

This is because your interface is empty; in a structural type system like Typescript, an empty interface is implemented by every class regardless of what members the class actually has. Consequently, your class doesn't just implement MyInterface<A, B>, it also implements MyInterface<boolean, string> and every other possible assignment of types to the type parameters of MyInterface, because it (trivially) has all of the members required to implement the interface with those type parameters, whether or not it is explicitly declared as doing so with an implements clause.

As such, Typescript cannot infer what A and B should be for test2 because literally any types would be compatible; for example, test2<boolean, string>(myInstance, (p) => { /* ... */ }) compiles with no errors and p is of type string.

If the interface has a member (and the class implements it) such that the A and B parameterising the class must be the same as parameterising the interface, then p's type will be inferred correctly in both functions:

interface MyInterface<A,B>{
    foo(x: A, y: B): void;
}

class MyClass<A,B> implements MyInterface<A,B> {
    foo(x: A, y: B): void {}
}

test2(myInstance, (p)=>{
    // p: number
}) 

Playground Link

Related