Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you dynamically extend a type in typescript?

Tags:

typescript

In javascript you can do this:

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.

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.

like image 246
Doug Avatar asked Feb 02 '14 01:02

Doug


People also ask

Can you extend a type in TypeScript?

Use an intersection type to extend a type in TypeScript, e.g. type TypeB = TypeA & {age: number;} . Intersection types are defined using an ampersand & and are used to combine existing object types. You can use the & operator as many times as necessary to construct a type.

Is TypeScript dynamically typed?

Quick answer: JavaScript is a dynamically typed language, but TypeScript is a statically typed language. Longer answer: In dynamically typed languages all type checks are performed in a runtime, only when your program is executing.

How do you add a property to an existing type in TypeScript?

Use an intersection type to add a property an an existing type in TypeScript, e.g. type Person = Employee & {myProperty: string} . Intersection types allow us to build up new types by extending them and are most commonly used to combine existing object types.

Should I use type or interface TypeScript?

Interfaces are most recommended for defining new objects or methods or properties of an object where it will receive a specific component. Hence interface works better when using objects and method objects. Therefore it is our choice to choose between types or interface according to the program needs.


4 Answers

Update: It sounds like you are after truly dynamic behaviour at runtime... i.e. you don't know it will be video, audio or 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

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.

// 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.

like image 178
Fenton Avatar answered Oct 21 '22 12:10

Fenton


You're not extending the type in 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).

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

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 Socket and you get all the joys of type safety.

like image 28
David Sherret Avatar answered Oct 21 '22 11:10

David Sherret


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

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

// Demo instance
let myExtendedObject: b = {
  prop1: 'text',
  prop2: 99
}
like image 3
ofir_aghai Avatar answered Oct 21 '22 12:10

ofir_aghai


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

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

like image 1
Code Whisperer Avatar answered Oct 21 '22 13:10

Code Whisperer