TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

Class satisfies type

Open sdegutis opened this issue 8 months ago • 5 comments

🔍 Search Terms

class satisfies

✅ Viability Checklist

  • [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • [x] This wouldn't change the runtime behavior of existing JavaScript code
  • [x] This could be implemented without emitting different JS based on the types of the expressions
  • [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • [x] This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
  • [x] This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals

⭐ Suggestion

Make the keyword satisfies modify the interface of a class when used after the class header.

📃 Motivating Example

We can add properties at runtime in a way that can statically be known about and well typed.

But currently there's no way to tell TypeScript about these properties. This feature request would solve that:

type PrefixedWith$<T> = T & { [K in keyof T as `$${K & string}`]: T[K] };

class Foo satisfies PrefixedWith$ {

  bar = 1;

  constructor() {
    for (const [key, value] of Object.entries(this)) {
      Object.defineProperty(this, '$' + key, { value });
    }

    this.$bar // now we can use this in constructors
  }

  qux() {
    this.$bar // and methods
  }
  
}

const foo = new Foo();
foo.$bar // and externally

💻 Use Cases

  1. What do you want to use this for?

Telling TypeScript about dynamically generated types known about statically.

  1. What shortcomings exist with current approaches?

You have to wrap the class constructor, and constantly cast this, either in method return value assertions or method this-parameters.

  1. What workarounds are you using in the meantime?

Giving up.

sdegutis avatar Mar 01 '25 14:03 sdegutis

Effectively #49509. The use case is allowing a class statement (or interface) to have dynamic keys. I don't think satisfies is necessarily the right syntax for this, but syntax is probably beside the point anyway.

jcalz avatar Mar 01 '25 15:03 jcalz

@jcalz similar but not the same feature. That one maps over some type. This one maps over this type.

sdegutis avatar Mar 01 '25 15:03 sdegutis

So then that one is effectively a prerequisite for this one, given that the circularity of this is an additional problem to solve over and above allowing dynamic keys.

jcalz avatar Mar 01 '25 16:03 jcalz

Sure I dunno 🤷

sdegutis avatar Mar 01 '25 16:03 sdegutis

The problem is that this is apparently circular; it implies the existence of $$$$$bar.

In cases where this isn't circular, it's already possible:

type PrefixedWith$<T> = T & { [K in keyof T as `$${K & string}`]: T[K] };

class Foo {
  bar = 1;
  constructor() {
    for (const [key, value] of Object.entries(this)) {
      Object.defineProperty(this, '$' + key, { value });
    }
  }

}
class BarWith$ extends Foo {
  qux() {
    this.$bar // and methods
  }

}

interface BarWith$ extends PrefixedWith$<Foo> { }

const foo = new BarWith$();
foo.$bar // ok

RyanCavanaugh avatar Mar 03 '25 22:03 RyanCavanaugh

This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

typescript-bot avatar Mar 23 '25 01:03 typescript-bot