Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3063,7 +3063,14 @@ export class Resolver extends DiagnosticEmitter {
let boundPrototype = classInstance.getMember(unboundOverridePrototype.name);
if (boundPrototype) { // might have errored earlier and wasn't added
assert(boundPrototype.kind == ElementKind.FunctionPrototype);
overrideInstance = this.resolveFunction(<FunctionPrototype>boundPrototype, instance.typeArguments);
let boundFuncPrototype = <FunctionPrototype>boundPrototype;
let overrideTypeParams = boundFuncPrototype.typeParameterNodes;
let numOverrideTypeParams = overrideTypeParams ? overrideTypeParams.length : 0;
let baseTypeArgs = instance.typeArguments;
let numBaseTypeArgs = baseTypeArgs ? baseTypeArgs.length : 0;
if (numOverrideTypeParams == numBaseTypeArgs) {
overrideInstance = this.resolveFunction(boundFuncPrototype, baseTypeArgs);
}
Comment on lines +3071 to +3073
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

should we allow this implement when the generic number are the same? I think it will crash also if the typing mismatched.
IMO, maybe we should forbidden all generic function in interface?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hmm, I see only one edge case in this scenario:

interface I {
  add<T, U>(x: T, y: U): U;
}

class C2 implements I {
  add<U, T>(x: T, y: U): U {
    return x;
  }
}

Copy link
Copy Markdown
Contributor Author

@Changqing-JING Changqing-JING Apr 4, 2026

Choose a reason for hiding this comment

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

Good point. If we follow C++ design:

  1. template function aka. generic function can't be member in interface
  2. template function also can't override virtual function in interface.

Shall we do the same?

I would prefer to follow C++ design because generic function in interface is a confused behaviour
But it may break some existing code on user side.
What do maintainers prefer?

}
}
if (overrideInstance) overrides.add(overrideInstance);
Expand Down Expand Up @@ -3439,6 +3446,19 @@ export class Resolver extends DiagnosticEmitter {
default: assert(false);
}
if (!member.is(CommonFlags.Abstract)) {
// A generic method cannot implement a non-generic interface method
// because monomorphization requires a concrete type to generate code,
// but virtual dispatch through the interface has no type arguments.
if (unimplemented.has(memberName)
&& member.kind == ElementKind.FunctionPrototype
&& unimplemented.get(memberName)!.kind == ElementKind.FunctionPrototype
) {
let memberTypeParams = (<FunctionPrototype>member).typeParameterNodes;
let ifaceTypeParams = (<FunctionPrototype>unimplemented.get(memberName)).typeParameterNodes;
let numMemberTypeParams = memberTypeParams ? memberTypeParams.length : 0;
let numIfaceTypeParams = ifaceTypeParams ? ifaceTypeParams.length : 0;
if (numMemberTypeParams != numIfaceTypeParams) continue;
}
unimplemented.delete(memberName);
}
}
Expand Down
9 changes: 9 additions & 0 deletions tests/compiler/override-typeparam-mismatch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"asc_flags": [],
"stderr": [
"TS2515: Non-abstract class 'override-typeparam-mismatch/CC' does not implement inherited abstract member 'foo' from 'override-typeparam-mismatch/I'.",
"TS2515: Non-abstract class 'override-typeparam-mismatch/DD' does not implement inherited abstract member 'bar' from 'override-typeparam-mismatch/J'.",
"TS2515: Non-abstract class 'override-typeparam-mismatch/C2' does not implement inherited abstract member 'foo' from 'override-typeparam-mismatch/I2'.",
"EOF"
]
}
39 changes: 39 additions & 0 deletions tests/compiler/override-typeparam-mismatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
interface I {
foo(x: i32): i32;
}

class CC implements I {
foo<T>(x: i32): i32 {
return x;
}
}

let c:I = new CC();
c.foo(1);

interface J {
bar(x: i32): i32;
}

class DD implements J {
bar<T>(x: i32): i32 {
return x;
}
}

let dd:DD = new DD();
dd.bar<i32>(1);

interface I2 {
foo<T, U>(x: i32): i32;
}

class C2 implements I2 {
foo<T>(x: i32): i32 {
return x;
}
}

new C2().foo<i32>(1);

ERROR("EOF");
Loading