【问题标题】:Emit an inferred mapped type to a .d.ts将推断的映射类型发送到 .d.ts
【发布时间】:2019-11-04 18:30:31
【问题描述】:

我在文件中有一个名为 Commands 的映射类型,如下所示:

// File: protocol.ts

import { CArgs, CRes } from '../utils';

type S = import('src/applications').Services;
export type Commands = {
    -readonly [P in keyof S]: {
        args: CArgs<S[P]>;
        res: CRes<S[P]>;
    };
};

// ... more declarations

推理非常复杂,Services 本身就是一个映射类型,它使用 UnionToIntersection 和更多奇特的东西来推断,类型来自许多不同的文件... TypeScript 是这样的:

type Commands = {
    Com1: {
        args: number;
        res: void;
    };
    Com2: {
        args: {foo: string};
        res: string[];
    };
    // ... etc ...
};

现在我想发出一个具有已解析类型.d.ts文件,如第二个sn-p所示,因此客户端不需要声明整个文件树即可推断方式。换句话说,我想“粘贴”从 TypeScript 推断的类型,替换 Commands 声明并删除所有导入语句。

我想我需要使用编译器 API,但我能找到的小文档不是很清楚。老实说,我什至不知道从哪里开始。

如何执行该任务?

我首先使用ts.createProgram() 创建一个Program,并给出文件名protocol.ts。我从那里访问了每个节点,所以我知道文件已加载。我创建了一个TypeChecker,并为几乎每个Node、记录checker.typeToString(type)checker.GetFullyQualifiedName(type.symbol) 等使用了.getTypeAtLocation(),但没有任何推断类型的痕迹。也许类型检查器或程序需要加载每个文件? (我想它会自己做)如果是这样,我如何从tsconfig.json 中的全局模式创建数组?

如您所见,我有点迷茫,因此欢迎任何帮助和指导。

非常感谢您。

【问题讨论】:

  • 老问题,但您有没有找到可靠的解决方案?我有完全相同的问题。以下适用于一些示例,但就像 vscode 一样,对较大的示例进行截断并扩展联合。我可以在这个答案中添加一些类似的技巧,但是对于dayjsstackoverflow.com/a/57683652/1057157
  • @erich2k8 不,不是。最后,我只是为这个特定的项目提出了这个想法。我仍然很好奇如何正确解决它,但似乎不是很简单。

标签: typescript typescript-compiler-api


【解决方案1】:

我几乎可以肯定有更好的方法可以做到这一点,但是按照编译器 API 的 incremental example,我能够获得 VSCode 显示的相同信息。

给定./src/mapped-types.ts 是:

type A = { a: 1, b: 2, c: 3 }

export type Mapped = {
    readonly [K in keyof A]: A[K]
}

以下代码将打印出来:

type Mapped = {
    readonly a: 1;
    readonly b: 2;
    readonly c: 3;
}
import * as ts from 'typescript'
import * as fs from 'fs'

const rootFileNames = ['./src/mapped-types.ts']

const files: ts.MapLike<{ version: number }> = {};

const servicesHost: ts.LanguageServiceHost = {
    getScriptFileNames: () => rootFileNames,
    getScriptVersion: fileName => files[fileName] && files[fileName].version.toString(),
    getScriptSnapshot: fileName => {
        if (!fs.existsSync(fileName)) {
            return undefined;
        }

        return ts.ScriptSnapshot.fromString(fs.readFileSync(fileName).toString());
    },
    getCurrentDirectory: () => process.cwd(),
    getCompilationSettings: () => ({}),
    getDefaultLibFileName: options => ts.getDefaultLibFilePath(options),
    fileExists: ts.sys.fileExists,
    readFile: ts.sys.readFile,
    readDirectory: ts.sys.readDirectory
};

// Create the language service files
const services = ts.createLanguageService(servicesHost, ts.createDocumentRegistry());

const index = fs.readFileSync(rootFileNames[0], 'utf-8').indexOf('Mapped')
const info = services.getQuickInfoAtPosition(rootFileNames[0], index)!
console.log(ts.displayPartsToString(info.displayParts))

获取快速信息的相关代码是here,不幸的是那里有很多我不明白的地方,所以我不确定如何为您指明正确的方向。

【讨论】:

  • 感谢您的回答。我会出去,但我会尽快继续这项工作。我尝试了ts-morph 并在那里留下了question。正如我在那里所说,代码几乎可以工作,但是当它们嵌套时,我得到了对其他类型名称的一些引用,而不是像内联那样获得完整的普通类型。我会探索你的建议,如果我取得了一些感兴趣的事情,我会告诉你,只是需要更多时间来完成其他事情。节日快乐。
猜你喜欢
  • 2021-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-02
  • 2023-02-26
  • 1970-01-01
  • 2021-05-27
  • 2018-03-02
相关资源
最近更新 更多