TypeScript
TypeScript copied to clipboard
Class satisfies type
🔍 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
- What do you want to use this for?
Telling TypeScript about dynamically generated types known about statically.
- 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.
- What workarounds are you using in the meantime?
Giving up.
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 similar but not the same feature. That one maps over some type. This one maps over this type.
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.
Sure I dunno 🤷
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
This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes.