如何合并打字稿模块定义

时间:2019-12-20 19:33:12

标签: javascript typescript

我有一个具有自定义.d.ts文件的JavaScript模块。让我们调用模块Foobar

foobar.js

function foobar (opts) {
  this.register = plugin => {
    plugin(this)
  }

  this.decorate = (prop, value) => {
    this[prop] = value
  }

  return this
}
export default foobar

foobar.d.ts

export interface FoobarPlugin {
  (inst: FoobarInst): void
}

export interface FoobarInst {
  register(plugin: FoobarPlugin): void
  decorate(prop: string, value: any): void
}

export default function foobar (): FoobarInst

我也有插件:

fuzzbuzz.js

function fuzzbuzz (inst) {
  inst.decorate('fuzzbuzz', true)
}

export default fuzzbuzz

fuzzbuzz.d.ts

import { FoobarInst } from '../foobar/foobar'

export default function fuzzbuzz (inst: FoobarInst): void

我将插件加载到模块中

index.ts

import foobar from './foobar/foobar'
import fuzzbuzz from './fuzzbuzz/fuzzbuzz'

const inst = foobar()

inst.register(fuzzbuzz)

inst.fuzzbuzz // -> true

我需要在fuzzbuzz.d.ts中添加什么才能更新FoobarInst类型定义?

我尝试了以下形式的变化:

declare module foobar {
  interface FoobarInst {
    fuzzbuzz: boolean
  }
}

我的tsconfig.json如下:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true
    "forceConsistentCasingInFileNames": true, 
    "resolveJsonModule": true,
  }
}

感谢您提供的任何帮助!

2 个答案:

答案 0 :(得分:2)

您要寻找的是module augmentation。我认为,import中缺少fuzzbuzz.d.ts语句以使TS能够识别出,给定的声明扩展/增强了已经存在的"foobar"模块:

fuzzbuzz.d.ts:

import { FoobarOpts } from "foobar"; // can be any import, preferrably one of "foobar" 

declare module "foobar" {
  // your module extensions
}

关于插件架构

插件扩展必须由编译器进行静态分析。这意味着,一旦调用 foobar或通过动态导入,就无法使TS扩展foobar.register(fuzzbuzz)

相反,当fuzzbuzz.d.ts / .ts的{​​{3}}或module resolution包含.d.ts作为编译输入时,该模块被视为增强模块项目目录中的文件。因此,将类型扩充和foobar.register(fuzzbuzz)放在一个模块中是有意义的,以便类型和运行时代码同步。一个最小的例子:

foobar.ts:

declare module "foobar" {
  // Plugin gets the options and possibly some internal "foobar" state
  type Plugin = (opts: FoobarOpts, state: {}) => void;

  interface FoobarOpts {
    foo: string;
    bar: number;
  }

  export default function foobar(opts: FoobarOpts): void;

  function register(plugin: Plugin): void;
}

my-plugin.ts:

import { register, Plugin, FoobarOpts } from "foobar";

const fuzzBuzzPlugin: Plugin = (opt: FoobarOpts, state) => {
  opt.fuzzbuzz; // fuzzbuzz available now.
};

// set type augmentation...
declare module "foobar" {
  export interface FoobarOpts {
    fuzzbuzz: boolean;
  }
}

// ... and run-time plugin extension in one module, so they go hand in hand
register(fuzzBuzzPlugin);

client.ts:

import foobar from "foobar";

foobar({ bar: 42, foo: "buh", fuzzbuzz: true }); // works with fuzzbuzz

答案 1 :(得分:0)

感谢@ ford04的好评。通过提供的链接,我能够解决我的特定目标。

我的场景的重要细节是foobarfuzzbuzz都是用JavaScript编写的,并且具有自定义的.d.ts定义文件。此外,foobar模块不使用declare module,而是在模块本机导出的单个函数上使用export default

解决方案代码为:

| - node_modules \
|   - foobar \
|     - foobar.js
|     - foobar.d.ts
|     - package.json
|   - fuzzbuzz \
|     - fuzzbuzz.js
|     - fuzzbuzz.d.ts
|     - package.json
| - index.ts
| - package.json

foobar.js

function foobar (opts) {
  this.register = plugin => {
    plugin(this)
  }

  this.decorate = (prop, value) => {
    this[prop] = value
  }

  return this
}

export default foobar

foobar.d.ts

export interface FoobarPlugin {
  (inst: FoobarInst): void
}

export interface FoobarInst {
  register(plugin: FoobarPlugin): void
  decorate(prop: string, value: any): void
}

export default function foobar (): FoobarInst

foobar / package.json

{  
  "main": "foobar.js",
  "types": "foobar.d.ts",
}

fuzzbuzz.js

function fuzzbuzz (inst) {
  inst.decorate('fuzzbuzz', true)
}

export default fuzzbuzz

fuzzbuzz.d.ts

import { FoobarInst } from 'foobar'

declare module "foobar" {
  interface FoobarInst {
    fuzzbuzz: boolean
  }
}

export default function fuzzbuzz (inst: FoobarInst): void

fuzzbuzz / package.json

{
  "main": "fuzzbuzz.js",
  "types": "fuzzbuzz.d.ts",
}

index.ts

import foobar from 'foobar'
import fuzzbuzz from 'fuzzbuzz'

const inst = foobar()

inst.register(fuzzbuzz)

inst.fuzzbuzz // -> true and no type error!

重要详细信息

  • package.json

  • 中指定类型定义
  • declare module '<library>'将合并声明,即使<library>不显式使用declare module(我相信无论TypeScript仍将其视为模块)。

    < / li>
  • 此原型扩充/修饰容易出错,因为仅将fuzzbuzz导入index.ts就会导致类型合并...即使您不调用{{1 }}。 @ ford04答案为此提供了更多上下文。因此,将类型扩充和foobar.register(fuzzbuzz)放在一个模块中是有意义的,以便类型和运行时代码同步。