【问题标题】:Is it possible to use generic type parameter in its own extends constrains?是否可以在自己的扩展约束中使用泛型类型参数?
【发布时间】:2020-09-29 23:09:39
【问题描述】:

我想在自己的约束中使用泛型类型参数。在打字稿中完全有可能吗?

我的代码在这里:

type Config<T> = {
    context: T;
};

type Hooks<T> = {
    hooks: T;
};

type FunctionWithThis<T> = (this: T, ...args: any[]) => any;

type RemoveThis<T extends Record<string, FunctionWithThis<any>>> = {
    [P in keyof T]: T[P] extends (...a: infer A) => infer R ?  (...a:A) => R: never
}

 const configure = <TContext extends Object, 
                    THooks extends Record<string, FunctionWithThis<TContext & THooks>>> // problem here
                   (config: Config<TContext> & Hooks<THooks>) => {
    const result = {
        get data() { return config.context; }
    };

    Object.entries(config.hooks).forEach((action) => {
        (result as any)[action[0]] = (...args: any[]) => action[1].call(config.context as any, ...args);
    });

    return result as { data: TContext; } & RemoveThis<THooks>;
};

const engine = configure({
    context: {
        foo: 12
    },
    hooks: {
        log() {
            console.log(this.foo); // this.foo is typed correctly here but I don't have access to another hooks
        },
        test(str: string) {

        }
    }
});

或者在typescript playground

我正在尝试创建一个配置函数,用于执行具有预定义上下文的一组函数。 我已经设法创建了一个简单的演示版本,但现在我希望能够从另一个钩子中调用我的钩子。例如。我想配置我的test 挂钩来调用log 挂钩。 为了实现这一点,我尝试将联合类型作为通用参数传递给“FunctionWithThis”类型:

FunctionWithThis<TContext & THooks>

但不幸的是,它并没有给我我想要的东西:我的钩子仍然无法使用智能感知上下文。当泛型参数用作自身的约束时,似乎将其解析为unknown

有办法克服吗?

实际上我还有更复杂的计划:我想为configure 函数和回调添加一个更通用的参数,并且还希望能够从钩子中调用回调,反之亦然。所以它看起来像这样:THooks extends Record&lt;string, FunctionWithThis&lt;TContext &amp; THooks &amp; TCallbacks&gt;&gt;&gt; 其中TCallbacksTHooks 之后的新通用参数

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    仅使用泛型很难解决您的具体问题。幸运的是,TypeScript 有一个名为 ThisType 的特殊魔法类型函数,在 microsoft/TypeScript#14141 中实现,它允许指定对象字面量方法的 this 上下文。

    我希望你的代码是这样输入的:

    type ConfigwithHooks<C, H> = {
        context: C;
        hooks: H & ThisType<C & H>; // Type of 'this' in hooks is C & H
    };
    
    const configure = <C, H>(config: ConfigwithHooks<C, H>) => {
        const result = {
            get data() { return config.context; }
        };
    
        Object.entries(config.hooks).forEach((action) => {
            (result as any)[action[0]] = (...args: any[]) => action[1].call(config.context as any, ...args);
        });
    
        return result as { data: C } & H;
    };
    

    我认为这是你想要的方式:

    const engine = configure({
        context: {
            foo: 12
        },
        hooks: {
            log() {
                console.log(this.foo);
                this.test("hello");
            },
            test(str: string) {
                this.foo - 5;
            }
        }
    });
        
    /* const engine: {
        data: {
            foo: number;
        };
    } & {
        log(): void;
        test(str: string): void;
    } */
    
    const data = engine.data;
    engine.log();
    

    我还没有研究过如何实施您的更复杂的计划,但我想确保您对问题中的代码有前进的道路。

    Playground link to code

    【讨论】:

    • 它就像一个魅力,谢谢。但是我注意到一件奇怪的事情:我在你的操场上有正确的智能感知,但出于某种原因,在 VS Code 和 VS 2019 中,configure 中的this 输入为any。这可能是 IDE 的问题吗?
    • 可能吗?不知道如何帮助您重现
    • 我刚刚从您的 Playground 复制代码并将其粘贴到一个新的 .ts 文件中。这很奇怪,因为我的 VS Code 使用与 Playground 中相同的打字稿版本:4.0.2
    • 您使用的是--strict 还是所有与 Playground 相同的编译器选项?请注意,ThisType 的链接问题表明您应该使用--noImplicitThis(我认为它包含在--strict 中)。如果这不是问题,我不知道如何进一步提供帮助。
    猜你喜欢
    • 2020-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多