我有一个旧的JavaScript库,可以与AMD
和CommonJS
一起使用。我忘记了影响以这种方式编写该库的原始资源,但是我的JavaScript库是按以下模式编写的。
(function(window) {
'use strict';
function getLib() {
var mylib = {};
mylib.getSomething = function() {
return 'x';
}
mylib.getCar = function(make, model, year) {
return {
make: make,
model: model,
year: year
};
}
return mylib;
}
if(typeof module === 'object' && module && typeof module.exports === 'object') {
module.exports = getLib();
} else {
if(typeof(mylib) === 'undefined') {
window.mylib = getLib();
}
if(typeof define === 'function' && define.amd) {
define('mylib', [], getLib());
}
}
})(this);
如何为此JavaScript库编写类型定义d.ts
文件?
我尝试如下编写d.ts
,但是它不起作用。在我的Angular应用中,放置了以下文件node_modules/@types/mylib/index.d.ts
。
interface Car {
make: string;
model: string;
year: number;
}
interface MyLib {
getSomething(): string;
getCar(make: string, model: string, year: number): Car;
}
declare module "mylib" {
export let mylib: MyLib
}
在我的控制器中,我只是尝试导入该库并将其调试到控制台,但是得到undefined
。
import {mylib} from 'mylib'; //IDE doesn't complain, seems to find the type def
export class MyPage {
constructor() {
console.log(mylib); //undefined
console.log(mylib.getCar('ford', 'f150', 2018)); //code won't reach here
}
}
请注意,JavaScript软件包mylib
不在NPM
上,而是一个私有存储库,并且我已经将其安装(例如npm install mylib --save
)到node_modules/mylib
。我之所以这样说是因为我不确定类型def文件应该放在哪里。
感谢您的帮助。
更新:
根据以下建议,我已将d.ts
文件修改为如下所示,并将其放置在mylib/index.d.ts
中。
declare module "mylib" {
let mylib: MyLib;
export = mylib;
}
现在,我可以通过两种方式导入库。第一种方式。
import mylib from 'mylib';
第二种方式。
import * as mylib from 'mylib';
在VS Code(IDE)中,没有抱怨(没有红线表示问题)。此外,我将tsconfig.json
修改为如下所示。
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"module": "commonjs",
"esModuleInterop": true,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2015.promise",
"es2017",
"dom"
]
}
}
运行ng build
时,我看到以下消息。
ERROR in node_modules/typescript/lib/lib.dom.d.ts(3268,88): error TS2344: Type 'SVGElementTagNameMap[K]' does not satisfy the constraint 'Node'. Type 'SVGSVGElement | SVGCircleElement | SVGClipPathElement | SVGDefsElement | SVGDescElement | SVGElli...' is not assignable to type 'Node'. Type 'SVGFEColorMatrixElement' is not assignable to type 'Node'. Types of property 'values' are incompatible. Type 'SVGAnimatedNumberList' is not assignable to type 'string[]'. Property 'includes' is missing in type 'SVGAnimatedNumberList'. node_modules/typescript/lib/lib.dom.d.ts(3626,85): error TS2344: Type 'SVGElementTagNameMap[K]' does not satisfy the constraint 'Node'. Type 'SVGSVGElement | SVGCircleElement | SVGClipPathElement | SVGDefsElement | SVGDescElement | SVGElli...' is not assignable to type 'Node'. Type 'SVGFEColorMatrixElement' is not assignable to type 'Node'. node_modules/typescript/lib/lib.dom.d.ts(10405,11): error TS2430: Interface 'SVGFEColorMatrixElement' incorrectly extends interface 'SVGElement'. Types of property 'values' are incompatible. Type 'SVGAnimatedNumberList' is not assignable to type 'string[]'. node_modules/typescript/lib/lib.dom.d.ts(14172,86): error TS2344: Type 'SVGElementTagNameMap[K]' does not satisfy the constraint 'Node'. Type 'SVGSVGElement | SVGCircleElement | SVGClipPathElement | SVGDefsElement | SVGDescElement | SVGElli...' is not assignable to type 'Node'. Type 'SVGFEColorMatrixElement' is not assignable to type 'Node'.
“有趣”的事情是,如果我注释掉import
然后运行ng build
或ng serve
,则编译错误就会消失。当应用程序处于实时重新加载模式时,我可以简单地取消注释import
,尽管出现编译错误,但我仍然可以使用应用程序中库的所有功能。
答案 0 :(得分:2)
在类型声明文件中,getSomething
和getCar
函数是名为mylib
的命名导出的成员,但在实现中,getSomething
和{{1} }本身就是模块的导出。编写声明文件的最干净方法是这样的:
getCar
(使用artem所说的导出分配是另一种选择。)
旁注:建议不要在declare module "mylib" {
export function getSomething(): string;
export function getCar(make: string, model: string, year: number): Car;
}
上手动创建文件,因为软件包管理工具(例如npm和yarn)希望控制整个node_modules/@types/mylib/index.d.ts
目录。相反,您可以将声明文件添加到原始node_modules
包中,或将实际的mylib
包发布到私有存储库中。或者,如果您只想在此Angular应用程序中使用类型声明,则可以将@types/mylib
文件放在Angular应用程序自己的目录中,并可以(1)设置baseUrl
and paths
options以便模块分辨率为index.d.ts
找到您的文件,或(2)为类型声明创建一个mylib
文件,并使用相对路径将其注册为依赖项,并将其注册为您的主package.json
中的文件。
答案 1 :(得分:1)
由于您提到了NPM,所以我假设使用mylib
的代码使用的是CommonJS模块分辨率,并且您正在节点中运行它(或者使用的是webpack之类的东西)。
然后,此导出变体生效:
module.exports = getLib();
这会整体导出mylib对象,并且不会引入任何名为mylib
的内容,因此会导入import
import {mylib} from 'mylib'.
在运行时未定义。
如何在TypeScript中使用这种导出很复杂,but starting from TypeScript 2.7,通常的工作方式是
declare module "mylib" {
let mylib: MyLib;
export = mylib;
}
使用--module=commonjs
和--esModuleInterop=true
将其导入为默认导出
import mylib from 'mylib';
更新
为避免全局范围内的名称冲突,最好在mylib
模块范围内声明mylib
中的所有类型,例如
declare module "mylib" {
export interface Car {
make: string;
model: string;
year: number;
}
export interface MyLib {
getSomething(): string;
getCar(make: string, model: string, year: number): Car;
}
export let mylib: MyLib
}
但是,您将必须在需要它们的每个模块中显式导入这些类型:
import mylib from 'mylib';
import {Car, MyLib} from 'mylib';