Reputation: 408
I'm trying to create my own types on which i can call functions using the dot syntax
.
for example:
let myOwnType: myByteType = 123211345;
myOwnType.toHumanReadable(2);
I want to archive the same behavior like number, array etc. I don't want to create my type using the call signature or constructor signature
So after looking into the typescript libary i saw this number interface:
interface Number {
/**
* Returns a string representation of an object.
* @param radix Specifies a radix for converting numeric values to strings. This value is only used for numbers.
*/
toString(radix?: number): string;
/**
* Returns a string representing a number in fixed-point notation.
* @param fractionDigits Number of digits after the decimal point. Must be in the range 0 - 20, inclusive.
*/
toFixed(fractionDigits?: number): string;
/**
* Returns a string containing a number represented in exponential notation.
* @param fractionDigits Number of digits after the decimal point. Must be in the range 0 - 20, inclusive.
*/
toExponential(fractionDigits?: number): string;
/**
* Returns a string containing a number represented either in exponential or fixed-point notation with a specified number of digits.
* @param precision Number of significant digits. Must be in the range 1 - 21, inclusive.
*/
toPrecision(precision?: number): string;
/** Returns the primitive value of the specified object. */
valueOf(): number;
}
The Problem is that i could not find where and how the function bodys gets defined. I was thinking there must be a class implementing the interface or something like this.
I came up trying something like this:
interface Bytes {
toHumanReadable(decimals: number): bytesToHumanReadable;
bytesAs(unit: string): string;
}
function bytesToHumanReadable (decimals: number) {
// convert bytes to GB, TB etc..
}
How could i create my own types and use them just like a normal type? I know that the Interface doesn't work either but it was my first guess.
Upvotes: 2
Views: 865
Reputation: 21
T.J. Crowder great answer i expanded a bit on it
interface myByteType extends Number {
toHumanReadable?(): string;
}
// Adding the implementation
Object.defineProperty(Number.prototype, "toHumanReadable", {
value() {
return this.toString(16);
},
enumerable: false, // This is the default, but just for emphasis...
writable: true,
configurable: true
});
let a: myByteType = 2124;
a.toHumanReadable();
with the questionmark syntax you have a workaround to use your own type with your defined function !
Upvotes: 1
Reputation: 1075735
The Problem is that i could not find where and how the function bodys gets defined.
They're defined by the JavaScript engine, as part of the standard library. You'll find them on Number.prototype
. Here's how it works: When you try to access a property on a primitive (for instance, someNumber.toString()
), the JavaScript engine looks for the property on the prototype of that primitive's object type (number => Number
, string => String
, etc.). If you're making a call, it calls the method, passing in either the primitive (in strict mode) or an object wrapper for the primitive (in loose mode).
You can't define your own primitive type, that can only be done at the JavaScript specification level.
You could create a Byte
class and have instances of it, but they'd be objects, not primitives.
class Byte {
constructor(public value: number) {
}
yourMethodHere() {
// ...
}
}
You can also add methods to Number.prototype
, although it's best not to do that in any library code you may share with others. (Within your own app or page it's mostly fine, just be sure to use names that are unlikely to be used by future features of the standard library.)
For instance, this adds a toHex
method to numbers:
// Adding it to the type
interface Number {
toHex(): string;
}
// Adding the implementation
Object.defineProperty(Number.prototype, "toHex", {
value() {
return this.toString(16);
},
enumerable: false, // This is the default, but just for emphasis...
writable: true,
configurable: true
});
Live example on the playground
Upvotes: 3
Reputation: 149
You're already halfway there with the bytesToHumanReadable
function but you're missing one key thing.
Since you want to add new functionality to numbers, you'll have to extend the base NumberConstructor
interface and implement the functionality yourself. Let's take a look at this in practice.
interface myByteType extends NumberConstructor = {
toHumanReadable: (decimals: number): string
}
This is simply to satisfy the TypeScript compiler. You still have to implement the functionality yourself by extending the Number
prototype like so:
Number.prototype.toHumanReadable = function(decimals: number) {
// convert bytes to GB, TB etc..
}
You can then use the toHumanReadable
function on any variable that extends the myByteType
interface like so:
const secondsSinceOpened: myOwnType = 1334244200
secondsSinceOpened.toHumanReadable(2)
This question is very similar to this one so you could check it for further reading.
Upvotes: -1