Ways to keep a Typescript library API declaration coherent with its implementation?

*爱你&永不变心* 提交于 2021-01-29 19:02:19

问题


Project structure description

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).

Content of 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;
}

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.

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;

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

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

The problem

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.

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;

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?


回答1:


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.

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



来源:https://stackoverflow.com/questions/64592205/ways-to-keep-a-typescript-library-api-declaration-coherent-with-its-implementati

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!