我在编写此文时使用的 TypeScript 版本是 3.7.3。
这个问题似乎应该集中在解决,然后在继续向语言添加更多功能和扩大源声明与声明之间的差距之前加以修复。这是一个相当大的问题!
搜索词
"typescript 声明文件限制"
"具有或正在使用私有名称"
"导出的类表达式可能不是私有的或受保护的"
"导出函数的返回类型具有或正在使用私有名称 'foo'。ts(4094)"
相关问题(在 Google 上搜索结果中的一小部分,按顺序排列)包括:
- Recursive definitions in mixins #29872 (@canonic-epicure)
- Generating type definitions for mixin classes with protected members #17744 (@fr0)
- Possible limitations of *.d.ts files #18791 (@johnnyreilly)
- False error TS4094: ... exported class expression may not be private or protected. #30355 (@a-student)
- Unexpected TS4094 with the build parameter
declaration: true
#17293 (@saschanaz) - Cannot create declaration files from static factory class returned from a function. #24226 (@rjamesnw)
- Default export of the module has or is using private name 'VueConstructor' gluons/vue-thailand-address#5 (@gluons)
- [v5] sequelize.import error TS4082: Default export of the module has or is using private name 'Bluebird' types/sequelize#176 (@billybarnum)
- Exported variable has or is using private name #6307 (@jeffschwartz)
- Difficulties with --declaration flag (error TS4025: Exported variable has or is using private name) #23110 (@evil-shrike)
- TS4082: Default export of the module has or is using private name 'Ember.Helper' typed-ember/ember-cli-typescript#133 (@dfreeman)
- Exported variable has or is using private name from external library #15947 (@ryansmith94)
- [TypeScript] Default export of the module has or is using private name 'VueConstructor' vuejs/vue#6999 (@gluons)
- Thrown "error TS4022: 'extends' clause of exported interface '...' has or is using private name ..." with --declaration flag and mixing imported export name with equal export interface name #16440 (@Manusan42)
- [bug?] Cannot find name 'EventEmitter', property 'channel' of exported interface has or is using private name 'EventEmitter'. #28754 (@trusktr)
- error TS4060: Return type of exported function has or is using private name #5284 (@tommyZZM)
- type export issue #23280 (@mjbvz)
- Return type of public static method from exported class has or is using private name 'BaseWithPlugins' gr2m/javascript-plugin-architecture-with-typescript-definitions#1 (@gr2m)
实际上,Google上的列表可以一直列下去。 (我尝试跳过任何实际识别为错误的 bug,无论是否已修复,但可能我错过了一个或两个,尽管绝大多数似乎是声明文件的限制)
解决方法
要解决上述所有问题,对于库来说,一种方法是让库作者将他们的 types
字段指向其源入口点。这将消除列出的问题,但有一些很大的缺点:
- 如果一个没有声明文件的下游消费者库(例如没有
dist/index.d.ts
但指向源文件的库)(例如src/index.ts
),如果该消费者的tsconfig.json
设置与库的设置不同,则可能导致类型错误(例如库在其开发过程中将strict
设置为false
,但消费者将其设置为strict
,而 TypeScript 会捕获库代码中的所有严格类型错误)。 - 这在 "types" field in package.json pointing to a
.ts
file in node_modules results in the file being compiled and type checked #35744 中有文档记录。 - 如果将
skipLibCheck
设置为 true,则消费者项目的编译仍会对(非声明的)库代码进行类型检查。 - 这在
skipLibCheck: true
is ignored when package'stypes
field points to a.ts
file (not a.d.ts
declaration file) #41883 中有文档记录。
有了这两个问题的具体说明,如果您知道您将在一个系统中工作,可以保证所有库及其消费者都将使用相同的编译器(例如定义编译器选项的 Deno,适用于所有人),那么将 package.json
和 types
字段指向源文件(因为无法生成声明输出)是目前允许不受支持的声明文件功能的解决方法。 但是,如果库作者不知道库消费者将使用哪些编译器设置,这将导致大问题:一切可能会在开发和测试期间正常工作,但下游用户最终会在使用不同设置的情况下报告类型错误!
建议
支持声明文件中的所有语言特性。
请务必。🙏 我非常喜欢你们 TypeScript 团队,你们已经使 TypeScript 带来了更好的代码开发体验。❤️ 我只需要 TypeScript 为支持 所有 语言特性提供声明即可。一些工作正在进行中,例如 xE20fX ,但总体而言,我认为太多的努力被放在了新的语言特性上,而声明输出却被抛在了后面。这给开发者带来了糟糕的使用体验,当他们的工作代码不起作用时(他们希望发布它作为库并打开 xE24nX 时),他们的代码就无法正常工作。我希望神奇的、了不起的 TS 团队意识到这对某人来说可能是痛苦的,他们在项目上工作了数月之久,最后却以无法修复的类型错误结束,当他们想要发布这个库作为库并使用 xE25nX 时,然后不得不放弃功能并重写他们的代码,或者尝试将 package.json 的 xE26nX 指向 xE27nX 文件,却只能面对 xE28fX 和 xE29fX 。我希望每种语言的新功能都有与之配对的工作测试来等效地生成声明输出。能否将这一点变成每个新语言特性的要求?就像单元测试一样?
当然,这需要一些想象力,但更重要的是,它需要一些纪律:不允许在没有声明平衡的情况下引入新功能。
检查表
我的建议符合以下准则:
- 这不会对现有的TypeScript/JavaScript代码造成破坏性改变
- 这不会改变现有JavaScript代码的运行时行为
- 这可以在不根据表达式的类型生成不同的JS代码的情况下实现
- 这不是一个运行时特性(例如库功能、带有JavaScript输出的非ECMAScript语法等)
- 这个特性将与TypeScript's Design Goals的其他部分保持一致。
9条答案
按热度按时间ijxebb2r1#
@RyanCavanaugh 我有一个大型的 React UI 库,是用 TypeScript 编写的。我真的不希望导出所有 props 的类型,因为有一个明确的方法可以获取组件的 props。这是我的例子:
如果人们需要访问 props,他们可以通过使用
React.ComponentPropsWithoutRef<typeof UIButton>
来实现。我认为这是一个关于私有接口的使用清晰的例子,因为它仍然可以访问,而不需要显式地处理导出类型。这个问题对我来说是个问题,因为我有一个单独的索引文件,我在那里定义了
export * from './UIButton';
和库中的每个其他组件,这里会出现类型的冲突,而且我真的不希望将所有的类型重命名为以组件名称为前缀的形式。zengzsys2#
#32028 中的更改可能导致在编辑器中代码运行正常,但在准备发布库时,在尝试制作声明文件时出现问题(由于OP问题列表中的问题)。(cc @sheetalkamat)
332nm8kg3#
当我使用
hoist-non-react-statics
时,我也得到了error TS4060: Return type of exported function has or is using private name 'WithLocale'
。这种正常模式在declaration: true
时会导致错误:然后,我想到了一个创建函数混入的方法,它返回一个类,在使用
declaration: true
时没有出现错误。我的问题是,使用第二种模式(具有
withLocaleMixin
方法返回类的方法)是否可以?还是会引发与第一种模式不同的行为? 🙇8tntrjer4#
我更新了原始帖子,增加了一个名为
Workaround
的部分,详细描述了如何当前解决所有列出的问题,但它也带来了相当大的缺点。daolsyd05#
保持讨论继续,摘自 #44360 :
我发现 TypeScript 目前分裂成两种语言非常令人沮丧。第一种是
TypeScript
本身,第二种是TypeScript+declaration files
。在第一种语言中有效的程序,在第二种中无效。这意味着,TypeScript
是TypeScript+declarations
的超集。您可以在
TypeScript
中表达更多的逻辑,但是TypeScript+declarations
是分发代码的标准(以避免重新编译源文件)。尽管这是一个众所周知的问题,但这种分裂并没有在任何地方进行文档记录,TypeScript 开发团队也没有提供关于它的很多反馈。这是一个被掩盖的事情,没有人想谈论这个。这个问题至少应该在文档网站上详细描述。
解决方案很简单 - 应该做出一个设计决策,使得
*.d.ts
文件创建与常规源文件完全相同的内部编译数据。为此,可能需要不同的声明文件格式,即*.d2.ts
当你不得不花费数小时将你的代码(在
TypeScript
中编译得很好)混合在一起,降低类型安全性,使其在TypeScript+declarations
语言中工作时,这是非常令人沮丧的。在许多情况下,这是根本不可能的。wmvff8tz6#
Documenting the difference sounds good, maybe somewhere here? https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html
kfgdxczn7#
是的,看起来是一个合适的地方。只需要明确一点,只有一部分常规的
TypeScript
特性在使用声明文件 emit 时才被支持。vq8itlhq8#
根据@trusktr在帖子中的建议,改变声明文件生成的方式,只剥离实现并保留类型,可能是解决问题的最佳方案。
同时,即使走向修复现有解决方案的道路,似乎大部分拼图都已经在那里了。
正如帖子中提到的,匿名类型可以用#30979解决。也就是说,“绿色”,准备合并的PR。
另一个部分与内部mixin类的类型用对象表示的事实有关。当然,这会导致错误。例如,对于这个mixin:
生成的声明是:
这导致错误
this is only available in the non-static method of the class
但是,还有一个“绿色”的PR:#41587,可以用来解决这个问题。这个PR还应该解决众所周知的私有/受保护方法的问题。有了这个PR,
MyMixin
的声明将如下所示:因此,我相信,修复这个工单是一个适当的优先级问题,其中没有不可抗拒的技术问题。
这个问题显然给很多人带来了很多困扰,而且很明显没有讨论修复它的意图。亲爱的TS团队,为什么这种态度?
thigvfpy9#
@RyanCavanaugh 我必须说,我也对这个感到失望。一个团队可能会花很长时间建立一个库,并假设“如果它编译了,它就会工作!”结果在打开
declarations
后发现他们有很多非常难以解决的错误。这并不直观,而且没有太多的文档说明有这样的限制。我很庆幸现在偶然发现了这个问题。