m instanceof MyMap is false for a class MyMap extends Map instance.
m instanceof Map is correct. Found while fixing #7570 (PR #7573); it is a
different bug and survives that fix — it reproduces with and without the
base-type annotation, so it is not a declared-type-lowering problem.
Repro
class MyMap<K, V> extends Map<K, V> {}
class Plain {}
class SubPlain extends Plain {}
const a = new MyMap<string, number>();
console.log("unannotated:", a instanceof MyMap, a instanceof Map);
const b: Map<string, number> = new MyMap<string, number>();
console.log("annotated:", b instanceof MyMap, b instanceof Map);
// control: an ordinary class hierarchy is fine
const c: Plain = new SubPlain();
console.log("plain annotated:", c instanceof SubPlain, c instanceof Plain);
$ node --experimental-strip-types inst.ts
unannotated: true true
annotated: true true
plain annotated: true true
$ ./inst
unannotated: false true <-- both wrong
annotated: false true
plain annotated: true true <-- ordinary classes are fine
So the intermediate/leaf class edge is what is lost, not the base edge: the
chain walk finds Map but not MyMap. The same holds for class MySet extends Set.
Notes for whoever picks this up
- Ordinary (non-native-base) inheritance is unaffected, so this is specific to
the super()-to-a-native-base wiring, not to instanceof in general.
CLAUDE.md's Known-weak areas entry on native base-class subclassing is the
right place to start: "a native base's surface is installed at super() time
and its parent edge lives in the class registry".
- The existing
test_gap_6325_map_set_subclass.ts only asserts
m instanceof Map, which is why this was never caught.
test_gap_7570_map_set_declared_base_type.ts deliberately asserts only
instanceof Map / instanceof Set, with an inline comment pointing here.
Whoever fixes this should tighten those two lines to also assert
instanceof MyMap / instanceof MySet.
m instanceof MyMapisfalsefor aclass MyMap extends Mapinstance.m instanceof Mapis correct. Found while fixing #7570 (PR #7573); it is adifferent bug and survives that fix — it reproduces with and without the
base-type annotation, so it is not a declared-type-lowering problem.
Repro
So the intermediate/leaf class edge is what is lost, not the base edge: the
chain walk finds
Mapbut notMyMap. The same holds forclass MySet extends Set.Notes for whoever picks this up
the
super()-to-a-native-base wiring, not toinstanceofin general.CLAUDE.md's Known-weak areas entry on native base-class subclassing is the
right place to start: "a native base's surface is installed at
super()timeand its parent edge lives in the class registry".
test_gap_6325_map_set_subclass.tsonly assertsm instanceof Map, which is why this was never caught.test_gap_7570_map_set_declared_base_type.tsdeliberately asserts onlyinstanceof Map/instanceof Set, with an inline comment pointing here.Whoever fixes this should tighten those two lines to also assert
instanceof MyMap/instanceof MySet.