2014-02-02 6 views
15

В 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 иметь что-то вроде этого.

Редактировать: см. Пример ответа, который я разместил ниже; что-то подобное работает. Я по-прежнему соглашусь с любым умным ответом, который позволяет мне каким-то образом скомпилировать что-то полезное для самого базового объекта.

+0

Это принципиально невозможно статически проверить. (см. проблему остановки) – SLaks

+0

Я знаю, но разве я не могу что-то использовать в словаре? Как и общедоступные каналы: {[name: string]: Socket} = {} ... data.channels.audio.read() ;. Всегда ли тип машинописного текста требует, чтобы свойства класса и свойства словаря считывались с помощью [] скобок? – Doug

+0

Вы всегда можете написать javascript, если TS не поддерживает его. –

ответ

11

Обновление: похоже, что вы после действительно динамичного поведения во время выполнения ... т. Е. Вы не знаете, что это будет video, audio или некоторые другие значения. В этом случае, когда у вас есть динамическое имя канала из какого-либо источника, вы должны использовать [] синтаксис - но есть еще некоторые

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 

Если у ou хочет сделать это действительно динамически, например, вы просто хотите добавить что-то в экземпляр, вы тоже можете это сделать, но наследование дает вам гораздо больше за ваши деньги.

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

Если вы решительно настроены на динамическом маршруте, общая картина в машинописном будет представлять структуру с интерфейсом, а не класс. Интерфейсы (и модули и перечисления) открыты, поэтому их можно расширить на несколько блоков. Вам нужно будет обеспечить, чтобы ваш интерфейс и реализация были одинаково расширены. Это метод, который вы используете для расширения встроенных объектов.

// 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'); 
} 

Это дает вам полное автоматическое завершение и проверку типа.

+0

Mm ... Я понимаю, о чем вы говорите, но обратите внимание на вопрос, который я не «перед рукой» знаю, какое имя канала я создаю, поэтому я не могу определить интерфейс (или расширить прототип) перед рукой; иначе я мог бы просто создать класс со статическими членами и сделать с ним. – Doug

+1

@ Doug Я понимаю, что вы говорите. Если вы хотите по-настоящему динамическое поведение, то для типа '' '' '' 'типа '' - для ситуаций, когда вы не будете знать возможные значения во время компиляции. – Fenton

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 и вы получить все радости от типа безопасности.

+0

Одним из примечаний к этому является то, что хотя вам нужно написать 'data.channels ['audio']. Read()' в TypeScript (имеет смысл, потому что машинопись не может проверить 'data.channels.audio'), в Javascript' data.channels.audio.read() 'будет отлично работать при использовании библиотеки. –

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, это не очень большое дело.

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

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

// Demo instance 
let myExtendedObject: b = { 
    prop1: 'text', 
    prop2: 99 
} 
Смежные вопросы