microsoft / microsoft/TypeScript

Suggestion: ClassMethodDecoratorContext's name property could pick up Value["name"]

Open
#54,850 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Needs More Info
Dominant language
Go
Stars
111k
Forks
14.4k
Avg merge
1d 19h
Merged PRs (30d)
117

Description

lib Update Request

Configuration Check

My compilation target is ESNext and my lib is the default.

Missing / Incorrect Definition

interface ClassMethodDecoratorContext<
    This = unknown,
    Value extends (this: This, ...args: any) => any = (this: This, ...args: any) => any,
> {
    // ...
    /** The name of the decorated class element. */
    readonly name: string | symbol;

    // ...

We could change the name field to: readonly name: Value extends { name: string } ? Value["name"] : (string | symbol);. Most of the time, Value["name"] is just string, so this would be a non-breaking change (if slightly less intuitive).

However, if I were to augment my method type definitions to have a name property, TypeScript could pick up on it:

Sample Code

export type NumberStringType = {
    repeatForward(s: string, n: number): string;
    repeatBack(n: number, s: string): string;
};

type NST_RF_raw = NumberStringType["repeatForward"]["name"]
//   ^? type NST_RF_raw = string

type MethodsOnlyType = {
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  [key: string | number | symbol]: (...args: any[]) => any
}

type NamedMethodsOnlyType<T extends MethodsOnlyType> = {
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  [Key in keyof T]: T[Key] & { name: Key }
}

type NST_RF_named = NamedMethodsOnlyType<NumberStringType>["repeatForward"]["name"];
//   ^? type NST_RF_named = "repeatForward"

type DefaultInferredName = ClassMethodDecoratorContext<
//   ^? type DefaultInferredName = string | symbol
  NumberStringType, NamedMethodsOnlyType<NumberStringType>["repeatForward"]
>["name"];

interface ClassMethodDecoratorContext_Named<
  This = unknown,
  Value extends (this: This, ...args: any) => any = (this: This, ...args: any) => any,
> {
  readonly name: Value extends { name: string } ? Value["name"] : (string | symbol);
}

type DirectName = ClassMethodDecoratorContext_Named<
//   ^? type Directname = "repeatForward"
  NumberStringType, NamedMethodsOnlyType<NumberStringType>["repeatForward"]
>["name"];

type InferredName = ClassMethodDecoratorContext_Named<
//   ^? type InferredName = string
  NumberStringType, NumberStringType["repeatForward"]
>["name"];

Obligatory Playground link

Motivation

I've been experimenting with method decorators for about a week now, to implement some aspect-oriented programming (or at least, my understanding of it). One facet I haven't yet cracked is removing the need for the method name on a decorator:

  class NST_Class extends NumberStringClass {
    @precondition<"repeatForward">(callForwardPre)
    repeatForward(s: string, n: number): string {
      return super.repeatForward(s, n);
    }
  }

Since the context has a name field of string | symbol type, I can't infer the name from that... I think.

I am perfectly willing to be wrong and have this whole ticket closed as invalid!

Documentation Link

The ECMAScript 2023 specification for Function.name says I'm possibly wrong here: it specifies the name field must be a string (specifically, "not a symbol"). This could be just a specification bug with the introduction of symbol keys, but I am not ready to assert that at this time.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the ClassMethodDecoratorContext definition and compare the proposed conditional name type with the ECMAScript Function.name specification. Use the linked Playground to verify the inferred results for the named and unnamed cases. Done means resolving whether the proposed lib-definition change is valid under the specification.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.