Skip to content

Allow type definition for public class methods without specifying a method name. #33505

@xylesoft

Description

@xylesoft

Search Terms

Class method signature typing.
Function signature typing.
Contract class methods.
Interface function method signature.

Suggestion

It would be useful to create a method (a function, not arrow function) signature without having to define the method name, allow typing for arguments and return types in interfaces. Currently you can only define such signatures for arrow functions, see below:

type Adapter<T, U extends Message> = {
    [P in keyof T]: () => Promise<U>
};

Which can be implemented for method declaration such as:

class ExampleAdapter1 implements Adapter<ExampleAdapter1, MessageTypeOne> {
    public readonly getExamples = async (): Promise<MessageTypeOne> => {
        return Promise.resolve(new MessageTypeOne());
    }
}

A function cannot be typed, only arrow functions can.

The issue arises if you wish to define a return type for your methods with AsyncGenerator<U>, arrow functions cannot be defined as a generator. Hence you are unable to define a method signature contract for your classes.

Use Cases

To be able to create an interface to a class which allows only a single (or more) yielding or returning datatypes for all methods, regardless of method name.

This allows for a stricter class definition, which can be used as a interface against an implementor or user of the class. For example, you may wish to have a service which all methods should respond with a specific extendable datatype. Meaning that the service class can be dropped inbetween two layers of an architecture. This should allow tslint/typescript to warn the developer of invalid service method definitions.

This new defintion should only apply to public methods in a class.

Examples

Example of implementation:

class Message {
    payload: any;
}

class MyMessage extends Message {}

/**
 * Example of possible syntax for type declaration
*/
type AdapterMethod<T extends Message> = function(): AsyncGenerator<T>;

type Adapter<T, U extends Message> = {
    [P in keyof T]: AdapterMethod<U>;
};

class MyAdapter implements Adapter<MyAdapter, MyMessage> {
    public async * getStuff(): AsyncGenerator<MyMessage> {
        yield new MyMessage();
    }
}

class MyIterator {
    public async iterate(method: AdapterMethod<MyMessage>): Promise<void> {
        for await (const message of method()) { // message is a MyMessage type
            //... doSomethingWithMessage(message);
        }
    }
}

const it = new MyIterator();
const ad = new MyAdapter();
it.iterate(async function* (): AsyncGenerator<MyMessage> {
    /**
     * Pipe generator yield to Iterator
     */
    yield* ad.getStuff();
});

Examples of definitions

type AdapterMethod<T extends Message> = function(): AsyncGenerator<T>;
type AdapterMethod<T extends Message> = function(arg: string): AsyncGenerator<T>;
type AdapterMethod<T extends Message> = function(): Promise<T>;
type AdapterMethod = function(): string;

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

Metadata

Metadata

Assignees

No one assigned

    Labels

    QuestionAn issue which isn't directly actionable in code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions