你能动态扩展打字稿中的类型吗?

时间:2014-02-02 01:56:41

标签: typescript

在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();

你能用打字稿做类似的事吗?

特别是我有兴趣参加一个类:

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.

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

我知道它有点像静态类型规则的一步,但我发现它有时候对API的好处很有用。

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

5 个答案:

答案 0 :(得分:12)

更新:听起来你在运行时真正的动态行为...即你不知道它将是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();

以前的答案......对于阅读这个问题的人来说很有用,他们不是在完全动态行为之后(但对道格来说很无用,抱歉!)

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

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

如果你想真正动态地做这件事,例如你只想向实例添加一些内容,你也可以这样做 - 但是继承可以为你提供更多的钱。

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);

如果您在动态路由上设置了死区,则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');
}

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

答案 1 :(得分:3)

您没有在示例中扩展类型 ...您只是动态地向对象添加属性。在TypeScript中,您无法动态地将属性添加到包含具有不同类型的属性的对象 - 除非您转换为any(在我看来,应该避免使用)。

我建议在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();

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

答案 2 :(得分:1)

现在还不可能,但是现在正在进行一场持续且积极的讨论,在这些活动中,我们正在进行各种活动。

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

答案 3 :(得分:1)

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

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

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

答案 4 :(得分:0)

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, () => { ... });

默认情况下,“任何”时间的任何属性似乎也都是“任何”,并且如果没有强制转换,你不会在其中获得任何类型的安全性,但是如果你正在编写一个要在javascript中使用的打字稿库,这不是什么大不了的事。