我目前正在探索一种方法的替代方法,该方法以前有效,但在JavaScript中具有一些复杂性,我希望避免这种方法.

所以,我重新考虑了一下打字层,我想出了一个非常接近我想要的东西.

唯一的问题是,我有一个错误,我无法摆脱without using ts-ignore.

有人知道我怎样才能在不 destruct 以下设计的情况下解决这个问题吗?

  • 我想避免实现和代码重复,所以TypeScrip需要从but I can afford some ts hack and complexity in the abstractions classes的JavaScript层推断类型
  • 在js:(System* => _SystemGameMiddle => SystemGameMiddle => _System => System)中,原型需要如下所示
  • 我可以在SystemGameMiddleSystem中添加类型复杂性,但不能在System*中添加
  • system.test应公开注入到类类型的所有组件.
type Constructor<T> = new ( ...args: never ) => T;
type ComponentType<T extends Component=Component> = Constructor<T>


// components
abstract class Component {}
class ComponentA extends Component { #id = Symbol() }
class ComponentB extends Component { #id = Symbol() }
class ComponentC extends Component { #id = Symbol() }

// systems
abstract class System<R extends ComponentType[]=ComponentType[]> {
    static config<
        const R extends ComponentType[],
    >(
        config: R
    ) {
        abstract class __System<RR extends ComponentType[]=R> extends this<RR|R> {}
        return __System;
    }

    declare test1: R;
    declare test2: R[number];
    declare test3: <T extends R[number]>() => T;

    public update() {
        this.test1;
    }
}

//🔴 try adding any "middleware" class between System and final systemA
//// @ts-ignore - this is not really a solution to use  @ts-ignore
abstract class SystemGameMiddle<R extends ComponentType[]=ComponentType[]> extends System.config(
    [ComponentA] // this middleware will intercept constructor to also add and make avaible ComponentA
)<R> {
    static override config<
        const R extends ComponentType[],
    >(
        config: R
    ) {
        abstract class _SystemGameMiddle<RR extends ComponentType[]=R> extends this<RR|R> {}

        // this will work for ts side, but add complexity on js side because need add static field to keep track of the original config and we also lost the extends of SystemGameMiddle
        // abstract class _SystemGameMiddle<RR extends ComponentType[]=R> extends super.config( config )<RR|R> {}
        return _SystemGameMiddle;
    }

    override update() {
        this.test1;
        this.test2;
        this.test3();
    }
}

// for the hight level, i hould not have typescript complexity, ts should be almost automaticly deduce the type with ths js side injected in static class field
export class System1 extends SystemGameMiddle.config(
    [ComponentB]
) {
}

export class System2 extends SystemGameMiddle.config(
    [ComponentB, ComponentC]
) {
}

// TEST:::
// js: proto should be (System1 => _SystemGameMiddle => SystemGameMiddle => __System => System)
declare const system1:System1;
system1.test1; //ts: expected: [] | [typeof ComponentA] | [typeof ComponentB]
system1.test2; //ts: expected:  typeof ComponentA | typeof ComponentB
system1.test3(); //ts: expected:  typeof ComponentA | typeof ComponentB
// js: proto should be (System2 => _SystemGameMiddle => SystemGameMiddle => __System => System)
declare const system2:System2;
system2.test1; // ts: expected:  [] | [typeof ComponentA] | [typeof ComponentB, typeof ComponentC]
system2.test2; //ts: expected:  typeof ComponentA | typeof ComponentB | typeof ComponentC
system2.test3(); //ts: expected:  typeof ComponentA | typeof ComponentB | typeof ComponentC

type SystemWith<
    R extends ComponentType,
> = System & { test3<T extends R>( ): T}; // with implements

function useSystem1( system:System ) {
    system.test1;
    system.test3;
}
function useSystem2( system:SystemGameMiddle ) {
    system.test1;
    system.test3;
}
function useSystem3( system:SystemWith<typeof ComponentB> ) {
    system.test1;
    system.test3();
}
useSystem1( system1 );
useSystem2( system1 );
useSystem3( system1 );

Playground

推荐答案

我看到的主要问题是,你对TypeScript的推理能力期望过高.在static config()方法中,返回一个本地声明的class,名为__System.你希望这个类有一个强类型来跟踪R类型参数. 但它似乎并没有这样做.很难告诉what它实际上是,因为智能感知将显示typeof __System,它不提供任何信息.

我在这里要做的第一步是显式地写出你想要的类型,看看编译器是否能确保你的值符合它们,而不是仅仅希望它们会是inferred. 如果还是不行,至少你能看到哪里出了问题.

为此,我认为__System类是一个abstract class constructor type类,它构造System个实例,并且有自己的config个方法.通过判断您的代码,它看起来如下所示:

type SystemCtor<RR extends ComponentType[]> = (
    abstract new <R extends ComponentType[]>() => System<R | RR>
) & {
    config<R extends ComponentType[]>(config: R): SystemCtor<R | RR>
};

注意SystemCtor<RR>是如何递归定义的,以及每个对config()的调用如何将RRR合并到一个union中.此外,我必须将其设置为intersection,因为无法在与config相同的对象类型中使用abstract new(请参见this comment on microsoft/TypeScript#36392).

现在,通过判断,我们似乎希望config()返回SystemCtor<R>,所以让我们这样做annotate:

abstract class System<R extends ComponentType[] = ComponentType[]> {
    static config<const R extends ComponentType[]>(
        config: R
    ): SystemCtor<R> { // <-- annotated the return type
        abstract class __System<RR extends ComponentType[] = R> extends this<RR | R> { }
        return __System;
    }

}

而且编译过程中没有错误.因此,尽管编译器没有inferR的依赖,但它很高兴允许对其进行注释.

一旦我们做了这些事情,或多或少就开始为你工作:

declare const system1: System1;
system1.test1; // [typeof ComponentA] | [typeof ComponentB]
system1.test2; // typeof ComponentA | typeof ComponentB
system1.test3(); // typeof ComponentA | typeof ComponentB

declare const system2: System2;
system2.test1; // [typeof ComponentA] | [typeof ComponentB, typeof ComponentC]
system2.test2; // typeof ComponentA | typeof ComponentB | typeof ComponentC
system2.test3(); // typeof ComponentA | typeof ComponentB | typeof ComponentC

对于您的特定用例,这可能是不够的,也可能不够,但关键是,如果推断的类型不能满足您的需求,您应该对类型进行更明确的控制.是的,这可能是乏味的,因为你最终会写出至少在概念上多余的东西,但它的好处是有助于表达你的实际意图和准确地指出问题.

Playground link to code

Typescript相关问答推荐

APP_INITIALIZER—TypeError:appInits不是函数

为什么typescript要把这个空合并转换成三元?

如果存在ts—mock—imports,我们是否需要typescpt中的IoC容器

为什么从数组中删除一个值会删除打字错误?

在TypeScrip的数组中判断模式类型

如何消除在Next.js中使用Reaction上下文的延迟?

对未到达受保护路由的路由环境做出react

获取一个TypeScrip对象,并将每个属性转换为独立对象的联合

`fetcher.load`是否等同于Get Submit?

常量类型参数回退类型

ANGLE NumberValueAccessor表单控件值更改订阅两次

如何在Angular 服务中制作同步方法?

TypeScrip-根据一个参数值验证另一个参数值

KeyOf关键字在特定示例中是如何工作的

三重融合视角与正交阵

类型脚本中函数重载中的类型推断问题

获取类属性的类型';TypeScript中的getter/setter

如何实现允许扩展泛型函数参数的类型

如何知道使用TypeScript时是否在临时工作流内运行

使用嵌套属性和动态执行时,Typescript 给出交集而不是并集