11

I know javascript uses duck typing and at first I thought this would make polymorphism easy compared to strongly typed languages like C#. But now my functions that take arguments are littered with things like:

if(myObj.hasSomeProperty())

or

if(myObj.hasSomeMethod())

or

if(isNumber(myParam))

etc.

This is really ugly to me. I come from a C# background and I find defined interfaces to be much better.

I'm wondering if I'm incorrectly attempting to apply strategies that are effective in statically typed languages and there's some better way to do this in javascript?

I know I could just not check, but tracking down javascript run time errors can be a nightmare since they don't always happen where the error is actually occurring in the code.

  • 2
    I think you might just be fumbling over the nature of a dynamically-typed language. You have to kind of get used to the mindset that a lot of errors will occur at runtime instead of compile time. If you feel the need to check if every argument is a number in every function that inputs numbers, it can become quite a burden (though maybe worthwhile if you are shipping a lib with safety being the top goal). For anything of scale, I find it kind of essential to just allow functions to fail if the wrong types are passed in. Instead a more productive focus might be on constructing tests. – user204677 Dec 26 '15 at 1:25
  • Where it might help to do these checks to make sure the types conform to the necessary interface requirements (checking to see if they have the required methods, e.g.) is in your most central and widely-used functions (ones with instability = 0 with the efferent/afferent coupling metric Martin provides). That should be a pretty small target. There are typically plenty of one-off local functions which are isolated in scope -- those probably don't need such a comprehensive set of runtime checks. They don't accumulate much complexity. – user204677 Dec 26 '15 at 1:29
  • Switch to Type Script. It's still duck typed, but supports static typing to detect many errors at compile time. – CodesInChaos Dec 26 '15 at 21:20
  • 2
    You've hit on duck-typing's single largest problem: its power derives from its weakness. If you want do do object oriented JavaScript, you just have to live with the run-time errors, and hope your unit tests find them soon after you create them :-( – Ross Patterson Dec 26 '15 at 21:53
  • @RossPatterson The OPs problem is with dynamic typing, not with duck typing. TypeScript and Go are both duck typed, yet avoid the OP's problem. The problem with duck typing is a different one, namely that you can have members which pass the duck test but don't fulfill the contract you expect. – CodesInChaos Mar 3 '16 at 12:38
11

How do you use duck typing in javascript without always checking for properties and methods?

Simple: don't always check for properties and methods.

In Ruby, what you are calling is called "chicken typing". In a dynamically duck-typed, language, you simply trust that the caller passes you a suitable object. It's the caller's job to honor his side of the contract.

I know javascript uses duck typing and at first I thought this would make polymorphism easy compared to strongly typed languages like C#.

You are confusing multiple orthogonal axis of typing here. There are four orthogonal axis of typing:

  • When: dynamic typing (types aren't known and checked until runtime) vs. static typing (types are known and checked before runtime)
  • What: duck typing (types are based on behavior), structural typing (types are based on structure), and nominal typing (types are based on name)
  • Can you see them? explicit typing (the types have to be explicitly annotated) vs. implicit typing (types are inferred)
  • strong typing vs. weak typing – you might have noticed that I didn't give this one a catchy title nor an explanation in parentheses, that's because unlike the seven terms above, which each have a single universally-accepted precise definition, these two terms have about a dozen semi-widely used vague definitions that contradict each other; ideally you should avoid these terms altogether, and if you must use them, precisely define them first

Since you mentioned C#: it is mostly statically typed, but supports dynamic typing through the type dynamic, it is mostly nominally typed, but anonymous types use structural typing, and syntactic patterns (such as LINQ query comprehension syntax) can be argued to be either duck-typed or structurally typed, it is mostly explicitly typed but supports implicit typing for generic type arguments and local variables (although the local variable case is rather strange compared to most other languages, because you cannot just leave the type out, instead you have to give it an explicit pseudo-type var, in other words, if you want an implicit type, you have to explicitly say so). Whether or not C# is strongly or weakly typed is a matter of which definition of the two terms you use, however, note that there can be plenty of runtime type errors in C#, especially because of unsafe array covariance.

I know I could just not check, but tracking down javascript run time errors can be a nightmare since they don't always happen where the error is actually occurring in the code.

Debugging is not an easy skill to learn. There are, however, techniques to make debugging easier, e.g. the Saff Squeeze is a technique described by Kent Beck which uses tests and refactoring for debugging:

Hit 'em High, Hit 'em Low:

Regression Testing and the Saff Squeeze

Kent Beck, Three Rivers Institute

Abstract: To effectively isolate a defect, start with a system-level test and progressively inline and prune until you have the smallest possible test that demonstrates the defect.

  • That hit em high hit em low link gets an http 500 for me, with "Page no longer available" as the human oriented message. – joshp Dec 26 '15 at 8:03
  • The threeriversinstitute.org domain seems to have been abandoned. – Bart van Ingen Schenau Dec 26 '15 at 10:11
  • Ah, damn. And it isn't even archived on the WayBack Machine. – Jörg W Mittag Dec 26 '15 at 10:34
  • How is the caller supposed to honor their side of the contract? It seems like there's no way to communicate (in code) what the parameters should be. Every function is of the form function fname(objParam, objParam, ...). Does this mean languages like javascript are wholly dependent on external documentation to communicate usage? – Legion Dec 26 '15 at 14:28
  • @Legion: documentation, good naming, common sense, tests as behavioral specifications, reading the source code, you name it. Note that this is actually not much different from weaker type systems such as C#'s or Java's: E.g. the meaning of the return value of IComparer<T>.Compare(T, T) is only clear from the documentation, not the type. And where in the type of java.util.Collections.binarySearch(java.util.List<T>) does it say that the … – Jörg W Mittag Dec 26 '15 at 15:28
1

I know I could just not check, but tracking down javascript run time errors can be a nightmare since they don't always happen where the error is actually occurring in the code.

Indeed, the typical practice is not to check. And, yes, this does mean that you'll get javascript errors which are reported elsewhere from the actual problem. But in practice, I don't find this to be a big problem.

When working in javascript, I'm constantly testing what I'm writing. In most code, I've got unit tests which automatically run every time I save my editor. When something unexpectedly goes wrong, I know almost immediately. I've got a very small area of code in which I might have made the mistake, since it's almost always the last thing I touched that has the mistake.

When I do get a runtime error, I've at least got the stack trace, and in the case of an in-browser error I've got the ability to go to any level of the stack trace and inspect the variables. It's typically easy to trace back where the bad value came from, and thus trace it back to the original problem.

If you are like me when I wrote primarily in statically typed languages, I wrote larger blocks of code before testing and I didn't have practice in tracing back a value where it came from. Programming in a language like javascript is different, you have to use different skills. I suspect programming like that seems way harder, because those aren't the skills you've developed working in other languages like C#.

Having said that, I think there is a lot to be said for explicit types. They are great for documentation and catching errors early. I think in future we'll see increasing adoption of things like Flow and Typescript which add the static type checking to the javascript.

0

I think you are doing the right thing, you just need to find the style that will be more pleasing to your eye. Here are some ideas:

  • Instead of if(myObj.hasSomeProperty()) you could use if( myobj.prop !== undefined ). This, BTW will work only in non-strict mode, in strict mode you'd have to use if( typeof myobj.prop !== 'undefined' ).

  • You can offload some of the type checking to separate validators. This has the benefit of being able to skip validation once the interfaces are mature, e.g. if( is_valid( myobject )), where is_valid begins with if( !DEBUG ) return true;.

  • Sometimes it makes sense to clone input into a canonical form, in which case you could collect the various validation targets into the cloning function/object. For exmaple, in my_data = Data( myobj, otherstuff ) the Data constructor could conveniently run all the various validations in a central place.

  • You could use some library that will (at a performance toll) streamline your type validation into something more elegant. Even if you won't take this route in the long term you may find it comfortable to get you smoothly into your own style. Some examples include xtype.js, type-check, validator.js, etc.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.