我有一个类,该类具有代表其 map 的已知元素的通用类型,如下所示:
abstract class Component { ... }
class Test<Known extends Component[]> {
components: Map<string, Component> ...
has<C extends Component>(comp: C): this is Test<[...Known, C]> {
...
}
}
我希望在判断可用组件时可以使用Test.has()
来更改其类型,但是,它会附加到类型而不是替换:
// test is Test<[AComp, BComp]>, so far so good
const test = new Test<[AComp, BComp]>();
const comp = new CComp();
if (sometest.has(comp)) {
// now test is "Test<[AComp, BComp]> & Test<[AComp, BComp, CComp]>"
}
基本上,test.has()
得到了正确的类型,但它不是替换它,而是"拥有"它.
我确实相信这符合TypScript引擎的预期,因为它不理解第二种类型实际上扩展了第一种类型,并认为它们应该被视为不同的事物.
我该如何告诉引擎第二个扩展了第一个,这可能吗?
使用可复制示例编辑:
abstract class Component {
}
class AComp extends Component {
aValue = 1;
}
class BComp extends Component {
bValue = 3;
}
type ClassOf<C extends Component> = new (...args: any[]) => C;
type TestGetResult<C extends Component, K extends Component[]> = C extends K[number] ? C : (C | undefined);
class Test<K extends Component[]> {
components = new Map<string, Component>();
constructor(components: Component[]) {
components.forEach(comp => this.components.set(comp.constructor.name, comp));
}
has<C extends Component>(comp: ClassOf<C>): this is Test<[...K, C]> {
return [...this.components.keys()].includes(comp.name);
}
get<C extends Component>(comp: ClassOf<C>): TestGetResult<C, K> {
return this.components.get(comp.name) as TestGetResult<C, K>;
}
}
// This class simulates us getting the Test instances from somewhere else in the code, their type is not immediately known when we return them but by
// using .has() we can infer some of it
class SomeStore {
list: Test<any>[] = [];
add(test: Test<any>) { this.list.push(test); }
getTestWithSomeComponent<C extends Component>(comp: ClassOf<C>): Test<[C]> {
for (let test of this.list) {
if (test.has(comp)) {
return test;
}
}
throw new Error('No test found');
}
}
///////////////////////////////////////////////////////////////////////////////////////
const store = new SomeStore();
const test1 = new Test<[AComp, BComp]>([new AComp(), new BComp()])
store.add(test1);
const testAfterGet = store.getTestWithSomeComponent(AComp);
if (testAfterGet.has(BComp)) {
const a = testAfterGet.get(AComp);
// Here is the issue
const b = testAfterGet.get(BComp) // at this point, testAfterGet has the 'and' type
console.log(a.aValue);
console.log(b.bValue); // b should not be undefined
}