保持 Typescript 库 API 声明与其实现一致的方法? [英] Ways to keep a Typescript library API declaration coherent with its implementation?

查看:18
本文介绍了保持 Typescript 库 API 声明与其实现一致的方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In my library named mylib I have an API declarartion file src/mylib.d.ts written by hand. There is a reason for write it manually: I want to design an API first and then to implement it (while the tsc with --declaration flag does the opposite - it generates an API declaration from implementation).

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(由 types":"./dist/index.d 引用.ts"package.json 中设置)或仅作为 @types/mylib 发布.

At the package build stage this file can be copied as dist/index.d.ts (to be referenced by "types":"./dist/index.d.ts" setting in package.json) or just published as @types/mylib.

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

The API implementation code located is in 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.

This file i entry point for bundler: esbuild src/api.ts --bundle --outfile=dist/index.js --format=esm.

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

As result there will be 3 files in npm tarball: dist/index.js, dist/index.d.ts and package.json

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

The problem is that everything declared in mylib.d.ts are independent from it's implementation. For example we can remove randomAnimal from api.ts and project still be compiled without any errors.

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

My current solution to this problem is next: I add this lines to the end of src/api:

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

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

And then i run tsc with --noEmit and "files": ["src/api.ts"] options.

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

If there will be incoherence between declaration and implementation I will see an error.

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

It's pretty working solution, but the question is: are there any ways to do it better? For example, without creating additional unexported constant?

推荐答案

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

This is a pretty strange way to do this. API (contract) first makes total sense, but instead of writing .d.ts files, just write types an interfaces and not your implementation.

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

I don't think writing the .d.ts files manually and then the implementation is a pattern that is Typescript really supports.

这篇关于保持 Typescript 库 API 声明与其实现一致的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆