-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Request: Allow abstract classes to implement MappedTypes that are instantiated with type parameters. #21326
Comments
Tagging @DanielRosenwasser . The reason for this (afaict) is that TS requires the implements/extends clauses to only allow interfaces (reasonable). And TS treats A shorter way of describing the request would be: allow abstract classes to contain this mapped type member, and then actually resolve this member at the concrete subclass point instead of eagerly. |
The usual advice here is to declaration-merge, e.g. class BaseDeferred<T> {
}
interface BaseDeferred<T> extends Deferred<T> { } Does that give you the behavior you want? |
In our case no. Just trying this out produces "message: 'An interface may only extend a class or another interface.'" The same issue exists. Namely a mapped-type that is instantiated with a type parameter is not viewed as an interface (understandable). But it would be nice if this could be relaxed. It appears as if mapped types are the only ones currently that go through this extra 'resolve the real members' step. It would be very nifty if interfaces could have this capability as well. |
Note: if some system for this does exist today, we'd def be happy to use it. but my TS-fu is a little too weak with all these new features to have figured out how to make this work. |
any news? I want to do something like this type IContext<T> = { [K in keyof T]: T[K] }
// Error: A class can only implement an object type or intersection of object types with statically known members.
class Context<T> implements IContext<T> { }
const ctx = new Context<{ id: number, name: string }>();
ctx.id
ctx.name |
@xxxTonixxx still not possible directly , might I suggest a workaround, use a separate constructor declaration that returns the apropriate intersection: class _Context<T> { /* members */ }
const Context: new<T>() => _Context<T> & T = _Context as any;
type Context<T> = _Context<T> & T
const ctx = new Context<{ id: number, name: string }>();
ctx.id
ctx.name |
It works! Thank you very much, good trick 😸 |
Any update on this? |
Just stumbled across this exact use case... |
Ok, so the title is a mouthful, but here's an example of what we're trying to accomplish:
Here we use mapped types to express the idea that a interface type might be wrapped with a new type that exposes the same properties as it, except as functions instead of data properties. So, for example:
We'd like to make a lot of our types deferable in our system, so we create a simple abstract base class that will help us out by doing some of the work for us. In other words, we'd like to be able to write:
We could then do the following:
At this point TypeScript would say: "Hey, DeferredPerson doesn't properly implement
BaseDeferred<Person>
, it is missingage: DeferredProperty<number>
andname: DeferredProperty<string>
. (And likewise for DeferredVehicle)However, this isn't currently allowed as we cannot say:
abstract class BaseDeferred<T> implements Deferred<T>
This is a somewhat understandable restriction. After all, how can the compiler actually validate that
BaseDeferred<T>
is implementingDeferred<T>
when it cannot know (at this point) how theDeferred<T>
lookup type will expand.While understandable, it would be nice if this restriction could potentially be lifted for abstract types. Because the type is abstract, we would like it if the check was only actually done at the time the type was concretely derived from. So, for example, when someone wrote:
--
The workaround today is to do the following:
The compiler now appropriately does the right checks. This is unpleasant though as it's a very simple thing to miss. Because all subclasses must implement this type, we would very much like to push that requirement up to the base class and have the enforcement applied uniformly across all subtypes.
--
Thanks much, and i hope everyone is doing great! We're having a blast with TS, especially (ab)using the type system to express some very interesting things. The more crazy stuff that can be expressed (especially around constraints and variadic types) the happier we are 🙂
The text was updated successfully, but these errors were encountered: