3

Example:

function action(value) {
     // I want to operate on a string
     String(value)...;
}

When we pass dynamic values into JavaScript's primary types (String, Number, Boolean, Object, etc.) we can (for want of a better word) cast the value to the specified type.

Is it possible to build this feature in custom types, and how would I go about this?

Example of what I would like to do:

function action(value) {
    Point(value)...;
    // Value at this point (excuse the pun) is a point
    // // *** Would like to see that intellisense is aware of the type at this point, but please don't assume this is ONLY for intellisense***
}

Is it possible to call constructor functions in this way and have the constructor function "cast" the value to an instance of itself - or does this only work for JavaScript's primary types?

Matthew Layton
  • 39,871
  • 52
  • 185
  • 313
  • it depends on the constructor parameter, if it can take a param of type string, int, etc then yes its possible – depperm Jul 02 '15 at 23:52
  • No, but you can create an object using `var o = Object.create(Point.prototype)` and then copy `value` into it. You can apply the constructor to it as well. `Point.call(o)` – Lye Fish Jul 02 '15 at 23:52
  • maybe your team is interested in TypeScript http://www.typescriptlang.org/ – bto.rdz Jul 02 '15 at 23:53
  • ...There are ECMAScript 6 features that allow more flexibility in this regard. – Lye Fish Jul 02 '15 at 23:53
  • You need to assign the result of the functions to something, e.g. `value = Point(value)`. – Barmar Jul 02 '15 at 23:53
  • @bto.rdz Love that you mentioned TypeScript, but I am already at a stage where I have submitted proposals for changes to the language spec. to the TypeScript team on GitHub, and I'm the TypeScript guru in my team. My question is purely related to JavaScript. – Matthew Layton Jul 02 '15 at 23:55
  • Please show exactly what you want the possible argument types to be for your `Point()` function. We need specifics in order to both understand exactly what you're trying to do and to offer good answers. – jfriend00 Jul 03 '15 at 00:16
  • Why do you want to do something like this? – Aadit M Shah Jul 03 '15 at 04:22

2 Answers2

3

Your custom constructor can just examine the typeof the arguments that it is passed and behave accordingly. This isn't technically a "cast", but rather writing code to examine the types of the arguments and then decide on the proper behavior which can include converting from one type to another.

See How to overload functions in javascript? for a much longer description of how to examine arguments sent to any function and then adjust the behavior of the function based on the type and position and presence of the arguments. This same functionality can be used to do something that is "cast" like (though we don't usually think of casting in Javascript, but rather just converting).

We could give you actual code examples if you can be more specific about what types you want to "cast" in your Point constructor.

There are some simple examples of "cast" like things:

function delay(fn, t) {
    // if t is passed as a string represeantation of a number, 
    // convert it to an actual number
    return setTimeout(fn, +t);
}

Or, a little more interesting example that can take a number of ms, a string with units at the end or an object with properties:

function delay(fn, t) {
    var typeT = typeof t, ms, matches, num, multiplier,
        suffixes = {ms: 1, sec: 1000, min: 1000 * 60, hr: 1000 * 60 * 60};
    if (typeT === "string") {
        matches = t.match(/^([\d.]+)(.+)$/);
        if (matches) {
            num = +matches[1];
            multiplier = suffixes[matches[2]];
            if (multiplier) {
                ms = num * multiplier;
            }
        }
    } else if (typeT === "number") {
        // plain number is milliseconds
        ms = t;
    } else if (typeT === "object" && t.units && t.value) {
        multiplier = suffixes[t.units];
        if (multiplier) {
           ms = t.value * multiplier;
        }            
    }
    if (ms === undefined) {
        throw new Error("Invalid time argument for delay()");
    }
    return setTimeout(fn, ms);
}


delay(myFn, "2.5hr");
delay(myFn, "25sec");
delay(myFn, 150);
delay(myFn, {units: "sec", value: 25});
Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Better copy it as a local variable e.g `var localT = +t;` to avoid overwriting arguments. – Sebastian Nette Jul 03 '15 at 00:13
  • @SebastianNette - there's no overwriting to be worried about here. The argument `t` is already local to this function so there's no need to create yet another local variable. – jfriend00 Jul 03 '15 at 00:14
  • `t = +t` overwrites `arguments[1]` which shouldn't be done, afaik (skips internal optimizations). – Sebastian Nette Jul 03 '15 at 00:19
  • "*we don't usually think of casting in Javascript, but rather just converting*". Absolutely. Variables are typeless, values have a [*Type*](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-ecmascript-data-types-and-values) (which often isn't related to the value returned by *typeof*). Anything returned by a constructor has a Type of Object, however that's pretty useless. Usually *constructor* is more informative, but unreliable. – RobG Jul 03 '15 at 00:21
  • @SebastianNette - that depends upon what you're optimizing for and what optimizations the exact JS engine is capable of. Are you seriously saying that it is worth adding an extra local variable declaration in a function this simple that has no performance importance? Or, are you just applying some blanket suggestion that doesn't really have any consequence in this specific example? – jfriend00 Jul 03 '15 at 00:23
  • @jfriend00—there's always `return setTimeout(fn, +t)`. ;-) – RobG Jul 03 '15 at 00:26
  • @jfriend00 Maybe it does not affect the performance of your posted example, but it's bad practice in general. I'm not saying that your answer is wrong in any way, just that it shouldn't be done like that. – Sebastian Nette Jul 03 '15 at 00:29
  • Another useful tool for this; A function can determine whether it's being used as a constructor or a function by checking `this instanceof MyFunctionName`. If true, it was called as `new MyFunctionName` – Katana314 Jul 03 '15 at 03:21
1

If you are serious about type enforcing (and there is a lot of good reasons to be)... and you want to continue to use Javascript... maybe you should give a try to TypeScript

It enforces type checking and compiles down to Javascript Which gives you the option to publish the TypeScript for inline compilation...

Or if you intent a bit for your work (like me)... you can develop a project and spit-out the Javascript before uploading... in this phase you can optimize the 'thing'... pass a Linter, a Minifier and a Obfuscator... and you'll get highly optimized and a bit protected piece of code (if we are talking about client-side of course, at server side no obfuscation is advised).

Another BIG-BIG-BIG advantage (to me at least) is that you can use the best Intellitype/Intellisense/Refactor IDE (VS 2013R4 CE) which is free.

See here the last features in TypeScript... TypeScript at Build/2014 by Anders Hejlsberg (channel9)

ZEE

ZEE
  • 2,931
  • 5
  • 35
  • 47