1

Apologise if the title is worded poorly.

What I am essentially tying to do is create a custom type which has methods on it. It's intended that this act as a generic type, so it can accept a string, int, etc

const foo = new CustomType('value')

console.log(foo) // 'value'
console.log(foo + 'hello') // 'valuehello'
foo.method() // Do something

const bar = new CustomType([])

bar.push('foobar')

If I use a class to store the value, I couldn't do operations on it.

class CustomType {
  constructor(value) {
    this.value = value
  }

  method() {}
}

const foo = new CustomType('value')
console.log(foo + 'hello') // [object Object]hello"

Similar to how you can use new Array() or new String()

David Alsh
  • 6,747
  • 6
  • 34
  • 60

2 Answers2

1

What you're trying to do is possible with prototypes (which is mimicked by classes in JS)

function CustomType(param) {
  this.default = param;
}

CustomType.prototype.toString = function(postfix = "") {
  return this.default + postfix;
};

CustomType.prototype.doSomething = function() {
  console.log("I am doing something")
};


let customTypedObject = new CustomType("value"); 
console.log(customTypedObject.toString());// "value
console.log(customTypedObject.toString("hello")); // "valuehello


//Prototypal function inherited
customTypedObject.doSomething() //"I am doing something"

Coercion Overriding

What you're actually explaining sounds a lot like overriding coercion rules in javascript which defines how a custom type behaves with another primitive type. This is also possible.

function CustomType(param) {
  this.default = param;
}
CustomType.prototype.valueOf = function() {
  return this.default;
}

let customTypedObject = new CustomType("value")
console.log(customTypedObject + "hello"); //valuehello

Read more here

If you are trying to override the browser's default approach to "toString()" an object. This is not possible. However there is a way to do this inside nodejs though. Node internally calls "inspect" on an object which is available in node js's root object prototype. This can be overriden

//Works on NodeJS code (not in browser)
CustomType.prototype.inspect = function() {
  return this.default;
};

let customTypedObject = new CustomType("value")
console.log(customTypedObject); // "value"
Dehan
  • 4,818
  • 1
  • 27
  • 38
-2

It's nearly impossible for the code, as given, to produce the desired results. When you do console.log(foo), for that to result in 'value' (and only 'value') being logged, foo can only contain the exact string 'value'. It can't even be 'value' wrapped in a String (or custom String) object.

console.log(new String('value'));

console.log, if passed a single variable, will log exactly what that variable passed to it is. If the variable is an object, it'll log that object, not anything else (like a string or a number). If the variable is a plain string or a number, then it won't have other methods on it, unless you mutate String.prototype or Number.prototype, which you should not do.

The best you can probably achieve would be to have an instance which, when coerced to a string, returns the desired stringified value:

class CustomType {
  constructor(item) {
    this.item = item;
  }
  toString() {
    return this.item;
  }
  method() {
    console.log('doing something');
  }
}

const foo = new CustomType('value')

console.log(foo) // CustomType {item: "value"}  - look in browser console, not snippet
console.log(foo + 'hello') // 'valuehello'
foo.method() // Do something

I said nearly impossible, not absolutely impossible. If you mutate the prototype of the passed object, you can put method on it to make foo.method() work:

const addCustomMethod = (item) => {
  Object.getPrototypeOf(item).method = () => {
    console.log('doing something');
  };
};


const foo = 'value';
addCustomMethod(foo);

console.log(foo) // 'value'
console.log(foo + 'hello') // 'valuehello'
foo.method() // Do something

But mutating built-in prototypes is a horrible idea. The above snippet is for informational purposes only, please do not use it.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • This is misleading. We can achieve the same result by altering a custom type objects prototype which is one of JS's most powerful features in the first place. – Dehan Feb 23 '20 at 03:11
  • My `toString` and `method` methods **are** putting custom methods on the prototype of a `CustomType`. That's not misleading at all. Your method of putting methods on the prototype by using `function` syntax and assigning to the `.prototype` object is doing the exact same thing that I'm doing. – CertainPerformance Feb 23 '20 at 03:16
  • I am talking about your second statement. You have stated as if altering the native prototype is the only option. When you can clearly modify the prototype of the custom type. – Dehan Feb 23 '20 at 03:47
  • Modifying the prototype of the custom type after an actual custom class has been defined doesn't really help, because you can just put the methods you need on the class when you define it, no mutation involved. The last snippet in the answer doesn't use any new classes at all, because modifying the built-in prototype is the only way that `'value'` can be logged while also using OP's original code verbatim. If you try to use a class there, the `console.log(foo)` will log the class object, not the `'value'` string. – CertainPerformance Feb 23 '20 at 05:55
  • "Modifying the prototype of the custom type after an actual custom class has been defined doesn't really help" <=== this is of course your opinion. Your thinking of prototypes in the classic class blueprint sense. The JS prototype was specifically designed for this run time overrides of the chain. But he is not talking about using an existing type verbatim. He wants to create a type from scratch in which case your argument is completely null an void. – Dehan Feb 23 '20 at 06:32
  • If you can define the method you need on the prototype in advance, what use is there in modifying it after the fact? I mean, sure, you can do so, but it doesn't do anything that couldn't be done more elegantly – CertainPerformance Feb 23 '20 at 06:34
  • "Read the question" he is trying to "create a type" not modify an existing type after the fact (facepalm) – Dehan Feb 23 '20 at 06:36