首页 > 解决方案 > 从模块名称列表中推断函数参数类型

问题描述

我有很多旧.js文件目前无法迁移到.ts文件中。我正在添加.d.ts文件以添加类型,但是使用类型是一个问题,因为文件通过 requireJs 相互导入,如下所示:

define(['moduleA', 'moduleB'], function(moduleA, moduleB) {
    // ...
});

我希望在这种情况下正确键入函数参数moduleA和。moduleB由于模块名称 ( 'moduleA', 'moduleB') 是在运行时配置文件中自定义的,TypeScript 无法真正帮助连接这些点。

所以我有一个想法是增加 的类型define,以便它需要来自第一个参数的模块名称列表,将它们映射到映射接口中定义的模块类型,然后将函数参数键入到第二个参数是模块类型列表。

我得到了基本的概念证明:

/**
 * This interface is used to map module names to their types.
 */
export interface RequireJsModules {
    foo: boolean;
    bar: string;
}

export type RequireJsModulify<T extends string[]> = {
    [P in keyof T]: T[P] extends keyof RequireJsModules
        ? RequireJsModules[T[P]]
        : unknown
};

// This line is for testing `RequireJsModulify`.
type Foo = RequireJsModulify<['foo', 'bar', 'wut']>;

此屏幕截图显示RequireJsModulify正在运行。Type ofFoo是基于模块名称元组的正确模块类型的元组。 Foo 类型的类型推断的屏幕截图是 [number, string, unknown]。

我坚持的是define函数签名:

declare global {
    export function define<T extends string[], U extends RequireJsModulify<T>>(
        deps: T,
        moduleFactory: ((...args: U) => any)
    ): void;
}

这个想法是,参数moduleFactory必须是基于类型参数的映射模块类型,类型参数T是字符串的任意列表。如果字符串是映射接口的键RequireJsModules,则相应的函数参数应该具有该模块类型,否则参数应该具有类型unknown

我有这个测试用例:

describe('requirejs type', () => {
    it('should call the module factory with the correct types corresponding to the dependencies', () => {
        // There should be no type errors below.
        define(['foo', 'bar', 'wut'], function(foo, bar, wut) {
            const fooExpected: boolean = foo;
            const barExpected: string = bar;
            const wutExpected: unknown = wut;

            console.log({ fooExpected, barExpected });
        });
    });
});

如果 for 的类型define按我想要的方式工作,那么第一个参数foo应该是 type boolean,第二个参数bar应该是 type string,而第三个应该是unknown

但他们都unknown在此刻。

因此:

关于将未知类型分配给布尔类型的类型错误的屏幕截图。

我正在使用 TypeScript 3.9.7。

任何想法为什么它不起作用?

标签: typescript

解决方案


推荐阅读