【问题标题】:Ways to keep a Typescript library API declaration coherent with its implementation?保持 Typescript 库 API 声明与其实现一致的方法?
【发布时间】:2021-02-11 23:04:13
【问题描述】:

项目结构说明

在名为mylib 的库中,我有一个手写的 API 声明文件 src/mylib.d.ts。手动编写它是有原因的:我想先设计一个 API,然后再实现它(而带有 --declaration 标志的 tsc 则相反 - 它从实现中生成一个 API 声明)。

src/mylib.d.ts的内容:

declare module "mylib" {
  export interface Animal {
    walk(): void;
  }
  export class Dog implements Animal {
    constructor(name: string);
    walk(run?: string): void;
    bow(): void;
  }

  export function randomAnimal(): Animal;
  export const version: string;
}

在包构建阶段,此文件可以复制为 dist/index.d.ts(由 package.json 中的 "types":"./dist/index.d.ts" 设置引用)或仅发布为 @types/mylib .

API实现代码位于src/api.ts

import * as mylib from "mylib";
import { Cat, Mouse } from "./other-animals"
import { logger } from "./logger"

export class Dog implements mylib.Dog {
  walk(run?: string): void {
    logger(`I'm a dog and i ${run ? "run" : "walk"}.`);
  }
  bow(): void {
    logger("bow-wow!");
  }
}

export function randomAnimal(name?: string): mylib.Animal {
  if (name) console.log("you passed name param. It wasn't documented but ok");
  // logger(mylib.version); //if you decomment this line, the bundler will
  // fail with error because "mylib" module doesnt really exists yet. It's OK because in
  // library source code i reference `mylib.d.ts` only for type imports.
  // Or we can just add "paths": { "baseUrl": "src", "mylib": ["./api.ts"] } to tsconfig.json so
  // bundler will use it to resolve module.
  return new Dog();
}

export const version = "1.0.0";
export const undocumentedVar = 123;

此文件是捆绑程序的入口点:esbuild src/api.ts --bundle --outfile=dist/index.js --format=esm

因此,npm tarball 中会有 3 个文件:dist/index.jsdist/index.d.tspackage.json强>

问题

问题在于 mylib.d.ts 中声明的所有内容都独立于它的实现。例如,我们可以从 api.ts 中删除 randomAnimal 并且项目仍然可以编译而没有任何错误。

我目前对这个问题的解决方案是下一个:我将这行添加到 src/api 的末尾:

import * as api from "./api";
const test: typeof import("mylib") = api;

然后我使用 --noEmit"files": ["src/api.ts"] 选项运行 tsc。

如果声明和实现之间存在不一致,我会看到一个错误。

这是一个非常有效的解决方案,但问题是:有什么方法可以做得更好吗?例如,不创建额外的未导出常量?

【问题讨论】:

    标签: typescript api-design


    【解决方案1】:

    这是一种非常奇怪的方法。 API(合同)首先是完全有意义的,但不要编写 .d.ts 文件,只需编写类型和接口,不是你的实现。

    我不认为手动编写 .d.ts 文件然后实现是 Typescript 真正支持的模式。

    【讨论】:

    • 查看 vscode 示例:github.com/microsoft/vscode/blob/master/src/vs/vscode.d.ts。这个文件肯定是手写的。然后它按原样发布到@types。
    • 是的,为已经存在的 .js 文件编写定义是有意义的。您尝试做的并不是一个得到很好支持或预期的工作流程。
    • 您能否就如何改变当前的项目结构提供建议?
    • 如果你在写打字稿,就写打字稿。您可以通过先编写接口和类型来避免编写实现。
    • 从 .ts 生成对应的 .d.ts 文件的问题是我主动使用 tsconfig 路径设置来避免从我的“../../../../”表达式中导入代码。打包器在生成 javascript 代码时解析此路径,但 tsc 在生成 .d.ts 文件时不解析。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-14
    • 1970-01-01
    • 2017-10-24
    • 2013-07-06
    • 2021-04-18
    • 2018-07-19
    • 1970-01-01
    相关资源
    最近更新 更多