我有一个具有自定义.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,
}
}
感谢您提供的任何帮助!
答案 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的好评。通过提供的链接,我能够解决我的特定目标。
我的场景的重要细节是foobar
和fuzzbuzz
都是用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仍将其视为模块)。
此原型扩充/修饰容易出错,因为仅将fuzzbuzz
导入index.ts
就会导致类型合并...即使您不调用{{1 }}。 @ ford04答案为此提供了更多上下文。因此,将类型扩充和foobar.register(fuzzbuzz)放在一个模块中是有意义的,以便类型和运行时代码同步。