microsoft / microsoft/TypeScript

Request: Allow abstract classes to implement MappedTypes that are instantiated with type parameters.

Aperta
#21,326 9 commenti 28 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

In Discussion Suggestion
Lingua principale
Go
Stelle
111k
Fork
14.3k
Merge medio
2g 4h
PR unite (30g)
132

Descrizione

Ok, so the title is a mouthful, but here's an example of what we're trying to accomplish:

interface DeferredProperty<T> {
    get(): T;
}

type Deferred<T> = {
    [P in keyof T]: DeferredProperty<T[P]>
};

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:

interface Person {
    age: number,
    name: string
}
var deferredPerson: Deferred<Person>;
var age = deferredPerson.age.get();


interface Vehicle {
    numWheels: number,
    cost: number
}
var deferredVehicle: Deferred<Vehicle>;
var cost = deferredVehicle.cost.get();

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:

abstract class BaseDeferred<T> implements Deferred<T> { //<-- note: currently not legal
    // some common stuff, including concreate methods

    // Maybe some abstract methods.
    // protected abstract whatever(): void;

    // etc.
}

We could then do the following:

class DeferredPerson extends BaseDeferred<Person> {
}
class DeferredVehicle extends BaseDeferred<Vehicle> {
}

At this point TypeScript would say: "Hey, DeferredPerson doesn't properly implement BaseDeferred<Person>, it is missing age: DeferredProperty<number> and name: 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 implementing Deferred<T> when it cannot know (at this point) how the Deferred<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:

class DeferredPerson extends BaseDeferred<Person> {
	// Now the compiler create the full type signature for BaseDeferred<Person> and then checked DeferredPerson against it.
}

--

The workaround today is to do the following:

abstract class BaseDeferred<T> { // <-- note: no implements
}

class DeferredPerson extends BaseDeferred<Person> implements Deferred<Person> {
}
class DeferredVehicle extends BaseDeferred<Vehicle> implements Deferred<Vehicle> {
}

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 🙂

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia con gli esempi di mapped-type e generici astratti presenti in questa issue, quindi esamina come il compilatore TypeScript gestisce le clausole implements delle classi generiche e le classi derivate. Confronta la forma proposta BaseDeferred con il workaround documentato. Il lavoro è completo quando le classi astratte possono posticipare il controllo del mapped-type, mentre le sottoclassi concrete vengono controllate rispetto al tipo istanziato, senza regressioni.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
typescript
Ambito
compilers
Tipo di issue
Funzionalità
Difficoltà
5/5
Tempo stimato
Più di una settimana
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
35/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.