Files
scriptc/tests/corpus/generic-interface-void-calls.ts
T
Chris Tate 693b038b15 Keep the result of void generic methods called through class-backed interfaces
A class may implement a void interface method with a method that returns a
value, and JavaScript passes that value on (`console.log(c.publish(m))`).
The call now keeps the dispatch's untyped result, as a call through a
non-generic void member already does, instead of replacing it with
undefined: printing or interpolating it shows the value, and binding it to
a void-typed name checks it.
2026-10-25 18:17:32 -05:00

66 lines
2.0 KiB
TypeScript

// Calling a void generic method through an interface value that classes
// implement (the receiver is a checked-dynamic object, and the call
// dispatches by name to the class's erased method) discards the result, as
// JavaScript does, instead of converting it to `void`.
interface Message<T = unknown> {
content: string;
details?: T;
}
interface Channel {
name: string;
publish<T = unknown>(message: Message<T>): void;
count<T>(items: T[]): number;
}
const delivered: string[] = [];
class LogChannel implements Channel {
name: string;
constructor(name: string) {
this.name = name;
}
publish<T = unknown>(message: Message<T>): void {
delivered.push(`${this.name}:${message.content}:${message.details === undefined ? "-" : JSON.stringify(message.details)}`);
}
count<T>(items: T[]): number {
return items.length;
}
}
class LoudChannel extends LogChannel {
publish<T = unknown>(message: Message<T>): void {
super.publish({ content: message.content.toUpperCase(), details: message.details });
}
}
const channels: Channel[] = [new LogChannel("quiet"), new LoudChannel("loud")];
for (const channel of channels) {
channel.publish({ content: "hello", details: [1, 2] });
channel.publish({ content: "bare" });
const result = channel.publish<string>({ content: "typed", details: "x" });
console.log(channel.name, result === undefined, channel.count(["a", "b", "c"]));
}
console.log(delivered.join(" | "));
// A class may implement the void member with a method that returns a value;
// JavaScript passes that value on.
class CountingChannel implements Channel {
name = "counting";
sent = 0;
publish<T = unknown>(message: Message<T>): number {
this.sent++;
return message.content.length;
}
count<T>(items: T[]): number {
return items.length * 2;
}
}
const counted: Channel[] = [new CountingChannel(), new LogChannel("plain")];
for (const channel of counted) {
console.log(channel.name, channel.publish({ content: "four" }));
console.log(`sent ${channel.publish<number>({ content: "again", details: 2 })}`);
}