Skip to content

allow class-scoped type declarationsΒ #55996

Closed
@martaver

Description

@martaver

πŸ” Search Terms

"class member types", "class scoped type members", "class level type declarations"

βœ… Viability Checklist

  • 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, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals

⭐ Suggestion

Allow the declaration of types within a class's instance scope that can use it's generic arguments, and be available to all members.

πŸ“ƒ Motivating Example

Sometimes we get many repetitive types in a class' definition.

Take for example:

interface IFoo<A, B> { foo(args: { value: A }): B }

class Foo<A, B> implements IFoo<A, b> {

  foo(...args: Parameters<IFoo<A, B>['foo']>): ReturnType<IFoo<A, B>['foo']> {
    throw new Error('Method not implemented.');
  }
}

Here we want to refer to the types of the generic IFoo that the class implements, but to access them we need to write the full IFoo<A,B> every time.

It would be handy to be able to create a class-scoped type alias, to be able to DRY this up:

interface IFoo<A, B> { foo(args: { value: A }): B }

class Foo<A, B> implements IFoo<A, b> {

  type F = IFoo<A,B>
  type Args = Parameters<F['foo']>
  type Ret = ReturnType<F['foo']>

  foo(...args: Args): Rer { // <-- clears up the signature, separates type definitions from implementation
    throw new Error('Method not implemented.');
  }
}

However, Typescript doesn't allow types to be declared in a class' scope.

For reference, the equivalent is possible in a closure:

interface IFoo<A, B> { foo(args: { value: A }): B }

function Foo<A, B>(): IFoo<A, B> {

  type F = IFoo<A, B>

  return {
    foo(...args: Parameters<F['foo']>): ReturnType<F['foo']> {
      throw new Error('Method not implemented.');
    }
  }
}

This would be a significant quality of life improvement, allowing developers to better express their intent.

πŸ’» Use Cases

  1. What do you want to use this for?
  • enhancing readaibility & expressiveness
  • publicly exposing utility types on class instances, e.g. type myType = Foo.F (potentially)
  1. What shortcomings exist with current approaches?
  • Developers who want to use classes are forced to write a lot of unnecessary types.
  1. What workarounds are you using in the meantime?
  • It's possible to use default generic arguments as a kind of type alias, but this is generally verbose and muddies intent.
  • Mostly, I just eat the extra keystrokes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions