简体   繁体   English

可扩展 Object 在 Typescript 定义

[英]Extensible Object In Typescript Definition

I have an object type that can be extended in javascript我有一个 object 类型可以在 javascript 中扩展

var myObject = Library.LibraryType();

myObject.myExtension = MyExtension();

As long as the extension adheres to some interface, the LibraryType can do useful things with it.只要扩展遵循某些接口, LibraryType就可以用它做一些有用的事情。

But how do I express this in typescript definition file?但是如何在 typescript 定义文件中表达呢? I don't want the users to have to cast everything to <any> all the time.我不希望用户必须一直将所有内容都转换为<any>

I wanted to do it with a dictionary.我想用字典来做。 But the compiler complains as all the other members don't adhere to the dictionary definition.但是编译器抱怨说所有其他成员都不遵守字典定义。

export interface IExtension {
}
export interface IExtensibleLibraryType {
    something: string;
    otherthing: (args: number) => void;

    [ keyOfExtension: string ]: IExtension;
}

It says all the other members are not IExtension .它说所有其他成员都不是IExtension

I would have thought the compiler would have just enforced that the dictionary members are accessed as dictionary mytype['keyOfExtension'] where all the other members could be accessed with the .我原以为编译器会强制将字典成员作为字典mytype['keyOfExtension']进行访问,其中所有其他成员都可以使用. notation?符号? Why is this not a thing?为什么这不是一回事?

Is there any way around this?有没有办法解决? Or do I just have to tell everyone to cast to any the whole time?还是我只需要告诉所有人一直投射到any Or force the users to extend the interfaces themselves in their own code (best option, but I bet people are more likely to do the former)?或者强迫用户在自己的代码中自己扩展接口(最好的选择,但我敢打赌人们更有可能做前者)?

I would really like it if the .d.ts could fully describe the API of the library like a useful addition to the docs.如果.d.ts能够完整描述库的 API,就像对文档的有用补充,我真的很喜欢。

TypeScript currently has better support for classical OO than for the approach you're using. 与您正在使用的方法相比,TypeScript当前对经典OO的支持更好。

class LibraryType {
    constructor() {
        // equivalent of your Library.LibraryType() function
    }

}

class MyExtendedVersion extends LibraryType {
    extension: blah
}

Sadly this requires you to change how your library works. 可悲的是,这需要您更改库的工作方式。 Instead of exporting a factory function, you have to export a class. 不必导出工厂功能,而必须导出类。 And internally your class's code will have to use this to access its own instance data. 在内部,您的类的代码将必须使用this来访问其自己的实例数据。

When migrating my own large-ish project to TS, I tried the classical conversion for a few files and quickly gave up with it. 将自己的大型项目迁移到TS时,我尝试了一些文件的经典转换,并很快放弃了。 It was a "rewrite" experience, which is bad because I need to make bug fixes in the previous version and merge them into the TS version. 这是一次“重写”的体验,这很糟糕,因为我需要在以前的版本中进行错误修复,然后将其合并到TS版本中。 It's much easier to migrate to TS if all you have to do is add a few type annotations here and there. 如果您要做的就是到处添加一些类型注释,则迁移到TS会容易得多。 So I use interfaces, $.extend and casting (type assertions) to avoid the need to rewrite. 所以我使用接口, $.extend和强制转换(类型断言)来避免重写。

There's a request for a language feature here that might help us in the future, but it's not on the roadmap so far. 这里要求提供一种语言功能,这可能会在将来对我们有所帮助,但是到目前为止,它并未在路线图上。

It says all the other members are not IExtension. 它说所有其他成员都不是IExtension。

The compiler is helping you here. 编译器在这里为您提供帮助。 If you say that anything accessed by a string should be of type IExtension then your own members (eg something ) need to conform to allow foo['something'] . 如果您说字符串访问的任何内容都应为IExtension类型,则您自己的成员(例如something )需要符合允许foo['something']

Fix: 固定:

move extensions one level down: 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 

For me, the easiest way of defining objects of type "particular type A plus something extra" would be like this:对我来说,定义“特定类型 A 加上一些额外的东西”类型的对象的最简单方法是这样的:

interface A {
  /* Whatever needed as a base */
}

type AExtended = {
  [K in keyof A]: A[K];
  extraField: string;
}

So, basically what you defining is an AExtended type, that besides all properties from A let's you define your own ones.因此,基本上您定义的是AExtended类型,除了A的所有属性之外,您还可以定义自己的属性。

[K in keyof A]: A[K];

Is a criptic way to say that for every key K from Mapped type A , it's type will be exactly same as for A[K] , and, because K is guarantee to be key from A , those types will be always equal.一种严格的说法是,对于映射类型 A中的每个键 K,它的类型将与A[K]的类型完全相同,并且因为K保证是A中的键,所以这些类型将始终相等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM