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

查看:64
本文介绍了如何保持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" >设置为/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.

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

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.js dist/index.d.ts package.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天全站免登陆