microsoft / microsoft/TypeScript
Interface that extends another no longer constrains types like the original
Nessuno ha ancora preso questa issue.
- Lingua principale
- Go
- Stelle
- 111k
- Fork
- 14.3k
- Merge medio
- 2g 4h
- PR unite (30g)
- 132
Descrizione
🔎 Search Terms
interface
interface constrain
extend interface
🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about interfaces.
⏯ Playground Link
💻 Code
/** SETUP ========================== */
function type<T>(): T { return null as unknown as T; }
type EventMap = Record<string, Event>;
type Listener<TEvent extends Event> = ((evt: TEvent) => void) | { handleEvent(object: TEvent): void };
export interface TypedEventEmitter<TEventMap extends EventMap> {
addEventListener<TEventType extends keyof TEventMap>(
type: TEventType,
listener: Listener<TEventMap[TEventType]>,
options?: AddEventListenerOptions | boolean,
): void;
removeEventListener<TEventType extends keyof TEventMap>(
type: TEventType,
listener: Listener<TEventMap[TEventType]>,
options?: EventListenerOptions | boolean,
): void;
}
/** END SETUP ========================== */
/**
* ✅ Sanity test
*/
type<TypedEventEmitter<{ foo: Event }>>() satisfies TypedEventEmitter<{ foo: Event }>; // ✅
// @ts-expect-error
type<TypedEventEmitter<{ bar: Event }>>() satisfies TypedEventEmitter<{ foo: Event }>; // ✅
/**
* ✅ Alias of the original type
*/
type CoolEventEmitter<TEventMap extends EventMap> = TypedEventEmitter<TEventMap>;
type<CoolEventEmitter<{ foo: Event }>>() satisfies TypedEventEmitter<{ foo: Event }>; // ✅
// @ts-expect-error
type<CoolEventEmitter<{ bar: Event }>>() satisfies TypedEventEmitter<{ foo: Event }>; // ✅
/**
* ❌ Interface that simply extends the original type
*/
interface CoolInterfaceEventEmitter<TEventMap extends EventMap> extends TypedEventEmitter<TEventMap> { }
type<CoolInterfaceEventEmitter<{ foo: Event }>>() satisfies TypedEventEmitter<{ foo: Event }>; // ✅
// @ts-expect-error
type<CoolInterfaceEventEmitter<{ bar: Event }>>() satisfies TypedEventEmitter<{ foo: Event }>; // ❌
/**
* ❌ Interface that extends the original type and adds stuff
*/
interface CoolInterfacePlusDispatchEventEmitter<TEventMap extends EventMap> extends TypedEventEmitter<TEventMap> {
dispatchEvent<TEventType extends keyof TEventMap>(ev: TEventMap[TEventType]): void;
}
type<CoolInterfacePlusDispatchEventEmitter<{ foo: Event }>>() satisfies TypedEventEmitter<{ foo: Event }>; // ✅
// @ts-expect-error
type<CoolInterfacePlusDispatchEventEmitter<{ bar: Event }>>() satisfies TypedEventEmitter<{ foo: Event }>; // ❌
/**
* ✅ Copy pasting the code instead of extending the interface works
*/
interface CopyPastedEventEmitter<TEventMap extends EventMap> {
addEventListener<TEventType extends keyof TEventMap>(
type: TEventType,
listener: Listener<TEventMap[TEventType]>,
options?: AddEventListenerOptions | boolean,
): void;
removeEventListener<TEventType extends keyof TEventMap>(
type: TEventType,
listener: Listener<TEventMap[TEventType]>,
options?: EventListenerOptions | boolean,
): void;
dispatchEvent<TEventType extends keyof TEventMap>(ev: TEventMap[TEventType]): void;
}
type<CopyPastedEventEmitter<{ foo: Event }>>() satisfies TypedEventEmitter<{ foo: Event }>; // ✅
// @ts-expect-error
type<CopyPastedEventEmitter<{ bar: Event }>>() satisfies TypedEventEmitter<{ foo: Event }>; // ✅
🙁 Actual behavior
As soon as the original interface is extended – whether the extender adds properties, does not add properties, uses the type parameter, or doesn't use the type parameter – I seem to lose the ability to constrain types in the same way as the original interface can.
🙂 Expected behavior
I would expect an interface that extends another to constrain types in exactly the same way as the original.
Additional information about the issue
No response
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Riproduci il comportamento usando il TypeScript Playground collegato e il codice nell’issue, confrontando l’interfaccia originale, l’alias di tipo e le interfacce che estendono. Traccia il percorso del controllo dei tipi per i vincoli generici e aggiungi un test di regressione; il lavoro è completato quando un’interfaccia che estende conserva il vincolo originale e i casi @ts-expect-error esistenti si comportano come previsto.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- typescript
- Ambito
- compilers
- Tipo di issue
- Bug
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Stato di attività
- Ferma
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 38/100