Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Class satisfies type #61324

Open
6 tasks done
sdegutis opened this issue Mar 1, 2025 · 8 comments
Open
6 tasks done

Class satisfies type #61324

sdegutis opened this issue Mar 1, 2025 · 8 comments

Comments

@sdegutis
Copy link

sdegutis commented Mar 1, 2025

πŸ” Search Terms

class satisfies

βœ… Viability Checklist

⭐ 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.

@jcalz
Copy link
Contributor

jcalz commented Mar 1, 2025

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.

@sdegutis
Copy link
Author

sdegutis commented Mar 1, 2025

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

@jcalz
Copy link
Contributor

jcalz commented Mar 1, 2025

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.

@sdegutis
Copy link
Author

sdegutis commented Mar 1, 2025

Sure I dunno 🀷

@RyanCavanaugh
Copy link
Member

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

@sdegutis
Copy link
Author

sdegutis commented Mar 3, 2025

Cool. But that requires a ton of boilerplate for every class and subclass, and is super error prone.

@sdegutis
Copy link
Author

sdegutis commented Mar 3, 2025

Also, the real world example I was using excludes $$$$bar via never when extends $${key} so that isn't an issue. Not sure how it factors into the feature request though.

@sdegutis
Copy link
Author

sdegutis commented Mar 3, 2025

I just want to do crazy magic in JavaScript and have TypeScript read my mind is that too much to ask 😭

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants