Converting an arbitrary value to a string in JavaScript is surprisingly nuanced. There are 3 common ways to convert v to a string:

v.toString(); '' + v; String (v);

Each of the 3 methods have tradeoffs and quirks. For example, if v is null or undefined:

const v = null ; '' + v; String (v); v.toString();

For another, more complex, example, consider the following object.

const v = { valueOf: () => null };

What do you get when you try to convert this object to a string?

v.toString(); '' + v; String (v);

That's because '' + v and String(v) are almost equivalent, except for how they handle valueOf() . String(v) doesn't attempt to convert v to a primitive before converting the value to a string, which is generally safer.

const v = { valueOf: () => { throw Error ( 'Oops!' ) } }; v.toString(); String (v); '' + v;

Using Archetype

The nuance of what happens when you're converting one value to a string is confusing enough. What happens when you have an embedded object with multiple arrays of strings that you need to convert? After all, recursion is hard.

I wrote Archetype to specifically address the problem of validating and casting complex JSON objects. Archetype is a full schema validation library, but it also exposes a neat to() function for converting individual values. Archetype calls to() internally.

const Archetype = require ( 'archetype' ); Archetype.to({ valueOf: () => null }, 'string' ); Archetype.to({ valueOf: () => '42' }, 'string' ); Archetype.to( new Date (), 'string' ); Archetype.to( 42 , 'string' ); Archetype.to( Number ( 42 ), '42' );

Archetype doesn't convert null or undefined ("nullish" values) to a string. That's intentional because when you get a null value in a request body, you usually want to treat it as null rather than as a string.

Archetype.to( null , 'string' ); Archetype.to( undefined , 'string' );

Complex Objects

Where Archetype really shines is casting complex objects that contain string-like values using the same rules as Archetype.to() . For example, suppose you have this complex object:

const obj = { tags: [ false , 42 ], comments: [ { time: new Date (), body: 'Hello' } ] };

Archetype can cast and validate all these deeply nested properties into strings:

const Comment = new Archetype({ time: 'string' , body: 'string' }).compile( 'Comment' ); const Type = new Archetype({ tags: [ 'string' ], comments: [Comment] }); new Type(obj);

Moving On