typescript 您可以在打字稿中动态扩展类型吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21506149/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-11 01:11:38  来源:igfitidea点击:

Can you dynamically extend a type in typescript?

typescript

提问by Doug

In javascript you can do this:

在javascript中你可以这样做:

function Test() {
  this.id = 1;
};

Test.prototype.customize = function(key, callback) {
  this[key] = callback;
};

var callback = function() { alert(this.id); };
var x = new Test();
x.customize('testing', callback);
x.testing();

Can you do a similar thing in typescript?

你能在打字稿中做类似的事情吗?

Particularly I'm interested in having a class like:

特别是我对开设这样的课程很感兴趣:

class Socket {
  ...
}

class Sockets {

  public addChannel(name:string):void {
    this[name] = new Socket(); 
  }

  ...
}

data = new Sockets();
data.addChannel('video');
data.addChannel('audio');

...

var audio = data.audio.read();
var video = data.video.read();
etc.

The compiler complains that there's no 'audio' or 'video' member on 'Sockets', and won't compile. Is there a way to work around that without having to manually define the properties on the container class?

编译器抱怨“套接字”上没有“音频”或“视频”成员,因此无法编译。有没有办法解决这个问题,而不必手动定义容器类的属性?

I know it kind of side steps the static typing rules, but I find it occasionally useful for API niceness to have something like this.

我知道它有点像静态类型规则,但我发现它偶尔对 API 友好性有用。

edit: See the example answer I posted below; something like that works. I'll still accept any clever answer that lets me somehow manage to compile something that does something useful on the base object itself.

编辑:请参阅我在下面发布的示例答案;像这样的工作。我仍然会接受任何让我以某种方式设法编译一些对基础对象本身有用的东西的聪明答案。

采纳答案by Fenton

Update: It sounds like you are after truly dynamic behaviour at runtime... i.e. you don't know it will be video, audioor some other values. In this case, where you have a dynamic channel name from some source, you would have to use []syntax - but there are still some

更新:听起来您在运行时追求真正的动态行为......即您不知道它将是videoaudio或其他一些值。在这种情况下,如果您有来自某个来源的动态频道名称,则必须使用[]语法 - 但仍然有一些

var channel = 'video;'

var data = new Sockets();
data.addChannel(channel);

// Totally dynamic / unchecked
var media = data[channel].read();

// Or

interface IChannel {
    read(): { /* You could type this */ };
}

// Dynamic call, but read method and media variable typed and checked
var media = (<IChannel>data[channel]).read();


Previous answer... useful for anyone reading this question who isn't after full on dynamic behaviour (but pretty useless for Doug, sorry!)

以前的答案......对于阅读这个问题的人很有用,他们不完全了解动态行为(但对道格来说毫无用处,对不起!)

If you want to extend an existing class, you can use inheritance:

如果要扩展现有类,可以使用继承:

class ClassOne {
    addOne(input: number) {
        return input + 1;
    }
}

// ...

class ClassTwo extends ClassOne {
    addTwo(input: number) {
        return input + 2;
    }
}

var classTwo = new ClassTwo();
var result = classTwo.addTwo(3); // 5

If you want to do this really dynamically, for example you just want to add something to an instance, you can do that too - but inheritance gives you much more for your money.

如果您想真正动态地执行此操作,例如您只想向实例添加一些内容,您也可以这样做 - 但继承为您提供了更多的钱。

class ClassOne {
    addOne(input: number) {
        return input + 1;
    }
}

// ...

var classOne = new ClassOne();

classOne['addTwo'] = function (input: number) {
    return input + 2;
};

var result = (<any>classOne).addTwo(3); // 5

//or (nasty 'repeat the magic string version')

result = classOne['addTwo'](3);

If you are dead set on the dynamic route, a common pattern in TypeScript is to represent the structure with an interface, not a class. Interfaces (and modules, and enums) are open - so they can be extended over multiple blocks. You would need to ensure your interface and implementation were equally extended. This is the technique you use to extend built-in objects.

如果你对动态路由一无所知,TypeScript 中的一个常见模式是用接口而不是类来表示结构。接口(和模块和枚举)是开放的 - 因此它们可以扩展到多个块。您需要确保您的接口和实现得到同等的扩展。这是用于扩展内置对象的技术。

// NodeList is already declared in lib.d.ts - we are extending it
interface NodeList {
    sayNodeList(): void;
}

NodeList.prototype.sayNodeList = function () {
    alert('I say node, you say list... NODE');
}

This gives you full auto-completion and type checking.

这为您提供了完全的自动完成和类型检查。

回答by David Sherret

You're not extending the typein your example... you're just dynamically adding a property to the object. In TypeScript you can't dynamically add a property to an object containing properties with different types--unless you cast to any(which, in my opinion, should be avoided).

您没有在示例中扩展类型……您只是向对象动态添加了一个属性。在 TypeScript 中,您不能向包含不同类型属性的对象动态添加属性——除非您强制转换为any(在我看来,应该避免这种情况)。

I would suggest adding a property to Sockets that is constrained to having properties who are of type Socketlike so:

我建议向 Sockets 添加一个属性,该属性被限制为具有如下类型的属性Socket

class Sockets {
    channels: { [index: string]: Socket; } = {};
    public addChannel(name:string):void {
        this.channels[name] = new Socket(); 
    }
}

var data = new Sockets();
data.addChannel('audio');

// audio will be the return type of read... so you don't lose type
var audio = data.channels['audio'].read();

By doing this you do not have to remember to cast the object to Socketand you get all the joys of type safety.

通过这样做,您不必记住将对象强制转换为对象,Socket并且您可以获得类型安全的所有乐趣。

回答by Code Whisperer

This is not possible yet but there is an ongoing and active discussion occurring right now among the Typescript community.

这是不可能的,但现在 Typescript 社区正在进行持续和积极的讨论。

https://github.com/Microsoft/TypeScript/issues/9

https://github.com/Microsoft/TypeScript/issues/9

回答by ofir_aghai

// Basic type
type a = {
  prop1: string
}

// Extended type
type b = a & {
  prop2: number
}

// Demo instance
let myExtendedObject: b = {
  prop1: 'text',
  prop2: 99
}

回答by Doug

class Blah {
  public channels:any = {};

  public addChan(name:string):void {
    this.channels[name] = new Channel();
  }
}

var b = new Blah();
b.channel('hello');
b.channels.hello.read(1024, () => { ... });

By default it seems any attribute of an 'any' time is also 'any', and you don't get any type safety in it without a cast, but if you're writing a typescript library to consume in javascript, it's not really a big deal.

默认情况下,似乎“任何”时间的任何属性也是“任何”,如果没有强制转换,您将无法在其中获得任何类型安全,但是如果您正在编写一个打字稿库以在 javascript 中使用,那并不是真的大不了。