Intersection of merged declarations - function types behave inconsistently
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 5/5
- Geschätzter Aufwand
- Über eine Woche
- Anfängerfreundlichkeit
- 35/100
- Issue-Typ
- Bug
- Klarheit
- Klar beschrieben
- Aktivitätsstatus
- Veraltet
- Tech-Stack
- typescript
- Bereich
- compilers
Rechercherichtung
Beginnen Sie damit, die verlinkte TypeScript Playground-Reproduktion auszuführen, und vergleichen Sie die zusammengeführten Foo- und Bar-Deklarationen mit dem Verhalten von Aufrufen auf Unions. Lesen Sie anschließend die Diskussion zu verwandtem Issue #19064 und verfolgen Sie den für zusammengeführte Funktionstypen und Unions verantwortlichen Type-Checking-Pfad. Als abgeschlossen gilt die Aufgabe, wenn das Ergebnis des Union-Aufrufs und ReturnType übereinstimmen und die spezifische shuffle-Methode beibehalten.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
🔎 Search Terms
declaration merging
🕗 Version & Regression Information
- This is a type inference problem
- This problem appears in Typescript 3.3.3, 3.5.1, 3.9.7, 4.9.5, 5.3.3 and 5.4.0-dev.20240207
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about declaration merging
⏯ Playground Link
💻 Code
type MyArrayExtension<T> = Array<T> & { shuffle(): void }
interface Foo { getList(): Array<string> }
interface Foo { getList(): MyArrayExtension<string> }
interface Bar { getList(): Array<string> }
interface Bar { getList(): MyArrayExtension<string> }
type FooListType = ReturnType<Foo["getList"]> // as expected: string[] & {shuffle(): void;}
type BarListType = ReturnType<Bar["getList"]> // as expected: string[] & {shuffle(): void;}
type FarListType = ReturnType<(Foo|Bar)["getList"]> // as expected: string[] & {shuffle(): void;}
let fooFn = ({} as Foo).getList; // { (): Array<string>; (): MyArrayExtension<string>; }
let barFn = ({} as Bar).getList; // { (): Array<string>; (): MyArrayExtension<string>; }
let farFn = ({} as Foo|Bar).getList; // { (): Array<string>; (): MyArrayExtension<string>; } | { (): Array<string>; (): MyArrayExtension<string>; }
let farList: FarListType = farFn(); // should work, but error:
/*
Type 'string[]' is not assignable to type 'MyArrayExtension<string>'.
Property 'shuffle' is missing in type 'string[]' but required in type '{ shuffle(): void; }'.(2322)
*/
({} as Foo).getList().shuffle(); // works
({} as Bar).getList().shuffle(); // works
({} as Foo|Bar).getList().shuffle(); // Error: Property 'shuffle' does not exist on type 'string[]'.(2339)
🙁 Actual behavior
There are two interfaces (Foo and Bar), that each are constituted of two merged declarations.
Each interface individually behaves such that the later declaration wins (getList(): MyArrayExtension<string> in either case). When changing the order (define the MyArrayExtension returning interfaces first), the return type accordingly is string[].
However, when intersecting Foo|Bar, the behaviour becomes inconsistent:
ReturnType<(Foo|Bar)["getList"]>is what I would expect (compatible withMyArrayExtension<string>), but({} as Foo|Bar).getList()is not assignable to a variable of this type
🙂 Expected behavior
My use case is the following: I have an external dependency that defines interfaces with functions that return T[].
Since I know those functions return a specific type that extends T[], I want to augment the module (using declare module) and give the functions a more specific return type.
The behaviour for non-intersected types if perfect for this. The later (e.g. provided by my declare module augmentation) wins.
However, it breaks down when intersecting two types from the external dependency, as described above.
I would expect type inference to work consistently. I think it's good to be able to override/make types more specific this way, and I think this ability should not be lost when intersecting types for which this has been done.
Additional information about the issue
@jcalz explained to me on StackOverflow that this problem is not restricted to module augmentation, but appears even when merging declarations in the same file. The problem is probably related to #19064.
- Vorherrschende Sprache
- Go
- Sterne
- 111k
- Forks
- 14.4k
- Ø Merge
- 1 T. 19 Std.
- Gemergte PRs (30 T.)
- 117
Beitragsleitfaden
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus microsoft/TypeScript
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 88/100
microsoft/TypeScript#64322 · 2 Kommentare · 1 Reaktion · 2 zugewiesene Personen ·
-
Possible Improvement
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
microsoft/TypeScript#64278 · 1 Kommentar · 1 Reaktion ·
-
Docs
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 70/100
microsoft/TypeScript#64118 · 1 Kommentar ·
-
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 88/100
microsoft/TypeScript#64094 ·
-
Docs
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 76/100
microsoft/TypeScript#63959 · 5 Kommentare ·
Alle Issues in microsoft/TypeScript
Ähnliche Issues
-
optimization optimization:agents-md-curator
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 86/100
githubnext/gh-aw-cao#13143 ·
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
blinklabs-io/bursa#904 ·
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
yanet-platform/ipfw-go#129 ·
-
bug confmap/provider/googlesecretmanagerprovider needs triage
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
open-telemetry/opentelemetry-collector-contrib#51273 · 2 Kommentare ·
-
bug: AI Gateway client filter lists "Unknown" twice when NULL and literal Unknown clients coexist Offenbug
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 90/100