我正在尝试创建一组外部TypeScript模块,如下所示:
// ClassA.ts
export default class ClassA {
public method() { return true; }
}
// ModuleB.ts
import ClassA from './ClassA';
var moduleB = {
ClassA: ClassA
};
export default moduleB;
// ModuleA.ts
import moduleB from './ModuleB';
var moduleA = {
moduleB: moduleB
}
export default moduleA;
想法是将其编译并作为库提供。 计划是库的使用者将实例化ClassA实例,如下所示:
import moduleA from './ModuleA';
var anInstance = moduleA.moduleB.ClassA();
看起来好像正在编译为预期的JS,但是我遇到了一个错误编译器错误,我一直在努力寻找有关的详细信息。
Using tsc v1.6.2
.../src/ModuleA.ts(3,5): error TS4023: Exported variable 'moduleA' has or is using name 'ClassA' from external module ".../src/ClassA" but cannot be named.
这里有人可以阐明这个问题吗? 我想做的事有意义吗?
哦-如果这不是问这个问题的合适地点。 请让我知道,我很乐意将其发布在其他媒体上。
FWIW通常会在Stack Overflow上获得更快的答案,但是我们在这里提出了措辞明确的问题。
这里的问题是您使用的是--declaration
标志,但是没有为编译器提供完成其工作的方法。
尝试发出ModuleA.d.ts
,编译器需要编写一个表示模块形状的对象类型文字(例如{ moduleB: { classA: *mumble?* } }
)。 但是作用域中没有一个直接指向classA
,因此它的类型为“无法命名”,并且存在错误。
如果将import
的ClassA
到ModuleA.ts
,错误应该消失了。
嗨,瑞安(Ryan)-感谢您的建议RE堆栈溢出和建议-将导入添加到ModuleA
按预期进行编译。
这里有一点背景-我的目标确实是为我的外部模块生成声明。 (我相信还不能正常工作吗?#5039?)这种导入可能是完全必要的,但是对于ModuleA
需要导入moduleB已经导出的符号,这有点奇怪。 结果是每次我将添加到ModuleB
的对象导出时,都需要将导入添加到ModuleA
-直接:
import {moduleB} from './moduleB';
import {ClassA} from './ClassA';
或通过ModuleB
(以减少在两个地方为ClassA
指定的路径):
import {moduleB, ClassA} from './moduleB';
我对TS编译器知之甚少-所以也许我说的是完全不现实的,但是起初以为编译器可以推断ModuleB
会导出一个带有N个符号的对象,因此ModuleA
需要这些符号吗? ModuleB
的导入信息已经存在- ModuleA
使用该信息吗? 还是那样是不可能的,因为对于进口ClassA
完全是内部ModuleB
,所以没有办法编译器可以推断出该类型ClassA
创建定义时, ModuleA
?
再次感谢您抽出宝贵的时间回答。 我已经解决了我的问题,因此以上内容主要是出于好奇-无需急于回答。
我不确定你在说什么。 此提案中的ModuleA.d.ts
应该是什么样?
编译器在发出声明时不会添加用户代码中不存在的依赖项(即import语句)。 您得到的错误意味着编译器正在尝试为导出的声明编写类型注释,但不能这样做。 这可能有两个原因之一,要么名称不可访问(即无法在当前模块中导入),要么存在覆盖原始声明的声明。
在这两种情况下,您的解决方法都是添加显式类型注释,如果添加任何类型注释,它将在输出中逐字显示; 另一个选择是确保名称是可访问的,即,您已经导入到模块,并且您了解这对于导入模块的用户意味着什么。
@mhegazy说:
编译器在发出声明时不会添加用户代码中不存在的依赖项(即import语句)。
问题是我并非总是需要代码中的import语句(显然,因为它在没有--declarations
情况下可以工作),并且包含它们很吵,导致诸如tslint
类的事情抱怨“未使用的导入”。 我可以看到为什么您不希望编译器将依赖项添加到发出的javascript中,但是将它们添加到发出的声明中又有什么问题呢?
请随时记录问题以跟踪此建议。 合理的理由是导入是对依赖项的声明,除非您指示这样做,否则编译器不应为您创建一个声明。
这可能会产生更深的后果。
考虑moduleA
-> moduleB
-> moduleC
-> moduleD
。
moduleB
是需要执行import { ABC } from 'moduleA'
来解决此问题的一个。
当moduleC
使用moduleB
并导出其签名时,它再次编译失败,因为moduleB
需要ABC
的moduleA
但是这次moduleB
没有导出。
这意味着:
moduleC
必须具有moduleA
的硬依赖性并导入ABC
moduleB
需要导入,还需要重新导出ABC
,然后moduleC
导入它。如果moduleD
做类似的事情,那么基本上您需要了解整个依赖链。
我无法对此进行测试以证明是这种情况,因为我正在使用typings
并且有一个问题阻止了我,所以: https :
编辑:我能够重现它,实际上我的moduleD
需要引用moduleA
来进行导入。
在我的示例中:
moduleA
=> redux
moduleB
=> redux-thunk
moduleC
=>在redux-thunk
之上的自定义代码moduleD
=>一些图书馆ABC
=> Dispatch
接口在redux
我有@unional描述的相同问题
请重新打开此问题, @unional概述的问题非常现实,这使得在使用与我们要重新导出的原始模块相同的类型的同时,很难在库顶部添加任何中间/辅助库。
发出https://github.com/Microsoft/TypeScript/issues/9944跟踪在声明发出阶段添加导入。
谢谢!
添加导入的问题在于,对于noUnusedLocals
,编译器将抱怨未使用该类型。 我可以添加一个显式的类型注释,但是这样我就不会得到推断。 例:
class Whatever {
fetch(uri: string): Promise<void> { }
ensureFetched = MemoizedFunction<(uri: string) => Promise<void>> = memoize((uri: string) => this.fetch(uri))
}
我想省略ensureFetched
类型注释
我找到了解决方法:
在tsconfig中: include: [ ..., "node_modules/@your_scope/your_library" ]
祝你好运,玩得开心:smiley:
@ salim7会减慢您的编译时间(因为您正在重新编译本应已编译的库),并迫使您对目标库使用严格性设置的最小公分母。
@mhegazy在下一个场景中重现了该问题:
import * as Foo from "./Foo";
export class Bar {
baz = new Foo.Baz(); // Compiler forgot "Foo." prefix in the type, and throws this error, because "Baz" without perfix is not imported.
getBaz() { // All the same
return new Foo.Baz();
}
}
解决方案是显式指定类型:
import * as Foo from "./Foo";
export class Bar {
baz: Foo.Baz = new Foo.Baz(); // ok
getBaz(): Foo.Baz { // ok
return new Foo.Baz();
}
}
我无法使用上面的示例来复制它。 请提交一个新的错误,并提供更多背景信息以重现该问题。
@PFight谢谢! 对我来说就是那样!
我刚刚在使用时遇到了这个问题:
export { IMyInterface } from './file'
````
The solution was to do this:
```ts
import { IMyInterface } from './file'
export { IMyInterface }
但这确实不是必需的。
有人知道如何处理noUnusedLocals
吗?
@yordis // @ts-ignore
@pelotom是的,这完全是我的错,
我在想一件事,然后写了另一件事。
Typescript是否解决了noUnusedLocals
和声明之间的问题?
我目前的解决方法,这让我感到非常痛苦,
import {SomeInterface} from "./SomeFile";
const _dummySomeInterface : undefined|SomeInterface = undefined;
_dummySomeInterface;
//Code that implicitly uses SomeInterface
避免使用noUnusedLocals
,并且尽可能允许通用接口的类型推断。
最有用的评论
FWIW通常会在Stack Overflow上获得更快的答案,但是我们在这里提出了措辞明确的问题。
这里的问题是您使用的是
--declaration
标志,但是没有为编译器提供完成其工作的方法。尝试发出
ModuleA.d.ts
,编译器需要编写一个表示模块形状的对象类型文字(例如{ moduleB: { classA: *mumble?* } }
)。 但是作用域中没有一个直接指向classA
,因此它的类型为“无法命名”,并且存在错误。如果将
import
的ClassA
到ModuleA.ts
,错误应该消失了。