简体   繁体   English

如何合并打字稿模块定义

[英]How to merge typescript module defintions

I have a JavaScript module that has a custom .d.ts file.我有一个带有自定义.d.ts文件的 JavaScript 模块。 Lets call the module Foobar :让我们调用模块Foobar

foobar.js foob​​ar.js

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

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

  return this
}
export default foobar

foobar.d.ts foob​​ar.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

I also have plugin:我也有插件:

fuzzbuzz.js fuzzbuzz.js

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

export default fuzzbuzz

fuzzbuzz.d.ts fuzzbuzz.d.ts

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

export default function fuzzbuzz (inst: FoobarInst): void

I load the plugin into my module:我将插件加载到我的模块中:

index.ts索引.ts

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

const inst = foobar()

inst.register(fuzzbuzz)

inst.fuzzbuzz // -> true

What do I need to add to fuzzbuzz.d.ts in order to update the FoobarInst type definition?我需要向 fuzzbuzz.d.ts 添加什么才能更新 FoobarInst 类型定义?

I have tried variations of:我尝试了以下变体:

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

My tsconfig.json looks like:我的tsconfig.json看起来像:

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

Thank you for any help you can provide!感谢您提供任何帮助!

What you are looking for is module augmentation .您正在寻找的是模块扩充 I think, an import statement is missing in fuzzbuzz.d.ts to let TS recognize, that the given declarations extend/augment an already existing "foobar" module:我认为, fuzzbuzz.d.ts缺少一个import语句让 TS 认识到,给定的声明扩展/增强了一个已经存在的"foobar"模块:

fuzzbuzz.d.ts: fuzzbuzz.d.ts:

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

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

Regarding plugin architecture关于插件架构

The plugin extensions have to be statically analyzable by the compiler.插件扩展必须可由编译器静态分析。 That means, you cannot make TS augment the foobar as soon as foobar.register(fuzzbuzz) is called or via dynamic import.这意味着,一旦foobar.register(fuzzbuzz)被调用或通过动态导入,你就不能让 TS 增加foobar

Instead, the module is seen as augmented, when fuzzbuzz.d.ts is included as input for the compilation either by module resolution or automatic inclusion of .ts / .d.ts files in the project directory.相反,当fuzzbuzz.d.ts通过模块解析自动包含.ts / .d.ts文件在项目目录中作为编译输入包含时,模块被视为增强的。 Therefore, it makes sense to place the type augmentation and foobar.register(fuzzbuzz) in one module, so that types and run-time code are in sync.因此,将类型增强和foobar.register(fuzzbuzz)放在一个模块中是foobar.register(fuzzbuzz) ,以便类型和运行时代码同步。 A minimal example:一个最小的例子:

foobar.ts: foob​​ar.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:我的插件.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:客户端.ts:

import foobar from "foobar";

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

Thank you @ford04 for a great reply.谢谢@ford04 的精彩回复。 With the links provided I was able to solve my specific goal.通过提供的链接,我能够解决我的特定目标。

The important detail with my scenario is both foobar and fuzzbuzz are authored in JavaScript, and have custom .d.ts definition files.我的场景的重要细节是foobarfuzzbuzz都是用 JavaScript 编写的,并且具有自定义的.d.ts定义文件。 Additionally, the foobar module does not use declare module but instead an export default on the singular function the module natively exports.此外, foobar模块不使用declare module ,而是使用模块本机导出的单一函数的export default

The solution code is:解决代码是:

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

foobar.js foob​​ar.js

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

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

  return this
}

export default foobar

foobar.d.ts foob​​ar.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 foob​​ar/package.json

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

fuzzbuzz.js fuzzbuzz.js

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

export default fuzzbuzz

fuzzbuzz.d.ts fuzzbuzz.d.ts

import { FoobarInst } from 'foobar'

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

export default function fuzzbuzz (inst: FoobarInst): void

fuzzbuzz/package.json fuzzbuzz/package.json

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

index.ts索引.ts

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

const inst = foobar()

inst.register(fuzzbuzz)

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

Important Details重要细节

  • Specify the type definitions in package.jsonpackage.json指定类型定义

  • declare module '<library>' will merge declarations even if the <library> does not use declare module explicitly (I believe it is still considered a module by TypeScript regardless).即使<library>没有明确使用declare module declare module '<library>'也会合并声明(我相信无论如何它仍然被 TypeScript 视为一个模块)。

  • This prototype augmentation / decoration is error prone in the sense that just by importing fuzzbuzz into index.ts will cause the types to merge...even if you do not call .register(fuzzbuzz) .这种原型增强/装饰很容易出错,因为仅通过将fuzzbuzz导入index.ts将导致类型合并……即使您不调用.register(fuzzbuzz) @ford04 answer provides more context on this Therefore, it makes sense to place the type augmentation and foobar.register(fuzzbuzz) in one module, so that types and run-time code are in sync. @ford04 回答提供了更多关于此的上下文因此,将类型增强和 foobar.register(fuzzbuzz) 放在一个模块中是有意义的,以便类型和运行时代码同步。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM