Typescript 定义中的可扩展对象
Extensible Object In Typescript Definition
我有一个可以在 javascript
中扩展的对象类型
var myObject = Library.LibraryType();
myObject.myExtension = MyExtension();
只要扩展遵循某些接口,LibraryType
就可以用它做一些有用的事情。
但是我该如何在打字稿定义文件中表达呢?我不希望用户必须一直将所有内容都转换为 <any>
。
我想用字典来做。但是编译器抱怨说所有其他成员都不遵守字典定义。
export interface IExtension {
}
export interface IExtensibleLibraryType {
something: string;
otherthing: (args: number) => void;
[ keyOfExtension: string ]: IExtension;
}
它说所有其他成员都不是 IExtension
。
我原以为编译器会强制将字典成员作为字典 mytype['keyOfExtension']
访问,而所有其他成员都可以使用 .
表示法访问?为什么这不是一回事?
有什么办法解决这个问题吗? 或者我是否只需要告诉所有人一直投射到any
? 或者强制用户在自己的代码中自己扩展接口(最好的选择,但我敢打赌人们更有可能做前者)?
如果 .d.ts
能够完整地描述库的 API,就像对文档的有用补充,我真的很喜欢。
It says all the other members are not IExtension.
编译器正在帮助您。如果您说字符串访问的任何内容都应该是 IExtension
类型,那么您自己的成员(例如 something
)需要符合允许 foo['something']
。
修正:
将 extensions
下移一级:
interface IExtension {
foo:string;
}
interface IExtensibleLibraryType {
something: string;
extensions: {[ keyOfExtension: string ]: IExtension};
}
function getLib():IExtensibleLibraryType{
return {
something:'',
extensions: {}
};
}
var test = getLib();
test.extensions['good'] = {foo:'test'};
test.extensions['bad'] = {foos:'test'}; // Error
TypeScript 目前对 classical OO 的支持比对您正在使用的方法更好。
class LibraryType {
constructor() {
// equivalent of your Library.LibraryType() function
}
}
class MyExtendedVersion extends LibraryType {
extension: blah
}
遗憾的是,这需要您更改图书馆的工作方式。您必须导出 class,而不是导出工厂函数。在内部,您的 class 的代码将不得不使用 this
来访问它自己的实例数据。
在将我自己的大型项目迁移到 TS 时,我尝试了几个文件的 classical 转换,但很快就放弃了。这是一次 "rewrite" 的体验,这很糟糕,因为我需要在以前的版本中修复错误并将它们合并到 TS 版本中。如果您所要做的只是在这里和那里添加一些类型注释,那么迁移到 TS 会容易得多。所以我使用接口,$.extend
和转换(类型断言)来避免重写的需要。
有一个 request for a language feature here 可能会在未来帮助我们,但目前还没有在路线图上。
对我来说,定义“特定类型 A 加上一些额外的东西”类型的对象的最简单方法是这样的:
interface A {
/* Whatever needed as a base */
}
type AExtended = {
[K in keyof A]: A[K];
extraField: string;
}
所以,基本上您定义的是 AExtended
类型,除了 A
中的所有属性之外,让我们定义您自己的属性。
[K in keyof A]: A[K];
是一种严格的说法,对于来自 Mapped type A 的每个键 K,它的类型将与 A[K]
完全相同,而且,因为 K
保证是键从 A
开始,这些类型将始终相等。
我有一个可以在 javascript
中扩展的对象类型var myObject = Library.LibraryType();
myObject.myExtension = MyExtension();
只要扩展遵循某些接口,LibraryType
就可以用它做一些有用的事情。
但是我该如何在打字稿定义文件中表达呢?我不希望用户必须一直将所有内容都转换为 <any>
。
我想用字典来做。但是编译器抱怨说所有其他成员都不遵守字典定义。
export interface IExtension {
}
export interface IExtensibleLibraryType {
something: string;
otherthing: (args: number) => void;
[ keyOfExtension: string ]: IExtension;
}
它说所有其他成员都不是 IExtension
。
我原以为编译器会强制将字典成员作为字典 mytype['keyOfExtension']
访问,而所有其他成员都可以使用 .
表示法访问?为什么这不是一回事?
有什么办法解决这个问题吗? 或者我是否只需要告诉所有人一直投射到any
? 或者强制用户在自己的代码中自己扩展接口(最好的选择,但我敢打赌人们更有可能做前者)?
如果 .d.ts
能够完整地描述库的 API,就像对文档的有用补充,我真的很喜欢。
It says all the other members are not IExtension.
编译器正在帮助您。如果您说字符串访问的任何内容都应该是 IExtension
类型,那么您自己的成员(例如 something
)需要符合允许 foo['something']
。
修正:
将 extensions
下移一级:
interface IExtension {
foo:string;
}
interface IExtensibleLibraryType {
something: string;
extensions: {[ keyOfExtension: string ]: IExtension};
}
function getLib():IExtensibleLibraryType{
return {
something:'',
extensions: {}
};
}
var test = getLib();
test.extensions['good'] = {foo:'test'};
test.extensions['bad'] = {foos:'test'}; // Error
TypeScript 目前对 classical OO 的支持比对您正在使用的方法更好。
class LibraryType {
constructor() {
// equivalent of your Library.LibraryType() function
}
}
class MyExtendedVersion extends LibraryType {
extension: blah
}
遗憾的是,这需要您更改图书馆的工作方式。您必须导出 class,而不是导出工厂函数。在内部,您的 class 的代码将不得不使用 this
来访问它自己的实例数据。
在将我自己的大型项目迁移到 TS 时,我尝试了几个文件的 classical 转换,但很快就放弃了。这是一次 "rewrite" 的体验,这很糟糕,因为我需要在以前的版本中修复错误并将它们合并到 TS 版本中。如果您所要做的只是在这里和那里添加一些类型注释,那么迁移到 TS 会容易得多。所以我使用接口,$.extend
和转换(类型断言)来避免重写的需要。
有一个 request for a language feature here 可能会在未来帮助我们,但目前还没有在路线图上。
对我来说,定义“特定类型 A 加上一些额外的东西”类型的对象的最简单方法是这样的:
interface A {
/* Whatever needed as a base */
}
type AExtended = {
[K in keyof A]: A[K];
extraField: string;
}
所以,基本上您定义的是 AExtended
类型,除了 A
中的所有属性之外,让我们定义您自己的属性。
[K in keyof A]: A[K];
是一种严格的说法,对于来自 Mapped type A 的每个键 K,它的类型将与 A[K]
完全相同,而且,因为 K
保证是键从 A
开始,这些类型将始终相等。