Check if a variable is a string in JavaScript

Asked 2023-09-20 20:25:54 View 620,703

How can I determine whether a variable is a string or something else in JavaScript?

Answers

This is what works for me:

if (typeof myVar === 'string' || myVar instanceof String)
// it's a string
else
// it's something else

// Test this approach:

let isString = value => typeof value === 'string' || value instanceof String;

let falseCases = [
  [ 'null', null ],
  [ 'undefined', undefined ],
  [ 'object', { a: 1, b: 2 } ],
  [ 'array', [ 1, 2, 3 ] ],
  [ 'number', 123 ],
  [ 'zero', 0 ],
  [ 'RegExp', new RegExp('hello') ],
  [ 'number with valueOf returning string', Object.assign(10, { valueOf: () => 'abc' }) ],
  [ 'object pretending to be string', { constructor: String } ]
];
let trueCases = [
  [ 'empty literal string', '' ],
  [ 'unicode string literal', String.fromCharCode(10000) ],
  [ 'empty boxed string', new String('') ],
  [ 'unicode boxed string', new String(String.fromCharCode(10000)) ],
  [ 'string with overwritten "constructor"', Object.assign('hi', { constructor: Array }) ],
  [ 'string with overwritten "toString"', Object.assign('hi', { toString: 123 }) ],
  [ 'string with overwritten "valueOf"', Object.assign('hi', { valueOf: 123 }) ],
  [ 'string with overwritten "constructor"', Object.assign('hi', { constructor: RegExp }) ],
  [ 'proxied string', new Proxy(new String('hello'), {}) ],
];

console.log('NEGATIVE TESTS:');
for (let [ name, val ] of falseCases) {
  console.log(`Test ${name}:\n  Expect: false\n  Got:    ${isString(val)}`); 
}

console.log('\nPOSITIVE TESTS:');
for (let [ name, val ] of trueCases) {
  console.log(`Test ${name}:\n  Expect: true\n  Got:    ${isString(val)}`); 
}

Answered   2023-09-20 20:25:54

  • Does "myVar instanceof String" do anything above and beyond "typeof myVar == 'string'" ? - anyone
  • @svth I remembered. In JavaScript you can have variable type of string or type of object which is class of String (same thing - both are strings - but defined differently) thats why is double checked. - anyone
  • var somevar = new String('somestring') console.log(typeof somevar) // object - anyone
  • -1 because the instanceof check here is pointless noise unless you're following some very unusual coding practices, and this answer does nothing to explain what it does or why you might use it. The only reason you'd ever need it is if you use object-wrapped strings, but object-wrapped strings are a worthless feature that nobody uses and Google and Crockford both condemn as bad practice (google-styleguide.googlecode.com/svn/trunk/…, crockford.com/javascript/recommend.html). - anyone
  • I strenuously disagree that writing solid code which correctly handles unlikely cases is something to be avoided. Checking both typeof and instanceof feels like good advice if your code may be called by others'. @MarkAmery's postmessage edge case matters if you're asking "what was I just postmessaged?" - but you'd expect that to be handled at the interface and not allowed to propagate. Elsewhere, it seems correct to handle non-deprecated coding methods even if some JS aesthetes disapprove of them. NEVER comment your code as accepting String, unless it truly does! - anyone

You can use typeof operator:

var booleanValue = true;
var numericalValue = 354;
var stringValue = "This is a String";
var stringObject = new String("This is a String Object");
console.log(typeof booleanValue) // displays "boolean"
console.log(typeof numericalValue) // displays "number"
console.log(typeof stringValue) // displays "string"
console.log(typeof stringObject) // displays "object"

Example from this webpage. (Example was slightly modified though).

This won't work as expected in the case of strings created with new String(), but this is seldom used and recommended against[1][2]. See the other answers for how to handle these, if you so desire.

// Test this approach:

let isString = value => typeof value === 'string';

let falseCases = [
  [ 'null', null ],
  [ 'undefined', undefined ],
  [ 'object', { a: 1, b: 2 } ],
  [ 'array', [ 1, 2, 3 ] ],
  [ 'number', 123 ],
  [ 'zero', 0 ],
  [ 'RegExp', new RegExp('hello') ],
  [ 'number with valueOf returning string', Object.assign(10, { valueOf: () => 'abc' }) ],
  [ 'object pretending to be string', { constructor: String } ]
];
let trueCases = [
  [ 'empty literal string', '' ],
  [ 'unicode string literal', String.fromCharCode(10000) ],
  [ 'empty boxed string', new String('') ],
  [ 'unicode boxed string', new String(String.fromCharCode(10000)) ],
  [ 'string with overwritten "constructor"', Object.assign('hi', { constructor: Array }) ],
  [ 'string with overwritten "toString"', Object.assign('hi', { toString: 123 }) ],
  [ 'string with overwritten "valueOf"', Object.assign('hi', { valueOf: 123 }) ],
  [ 'string with overwritten "constructor"', Object.assign('hi', { constructor: RegExp }) ],
  [ 'proxied string', new Proxy(new String('hello'), {}) ],
];

console.log('NEGATIVE TESTS:');
for (let [ name, val ] of falseCases) {
  console.log(`Test ${name}:\n  Expect: false\n  Got:    ${isString(val)}`); 
}

console.log('\nPOSITIVE TESTS:');
for (let [ name, val ] of trueCases) {
  console.log(`Test ${name}:\n  Expect: true\n  Got:    ${isString(val)}`); 
}


  1. The Google JavaScript Style Guide says to never use primitive object wrappers.
  2. Douglas Crockford recommended that primitive object wrappers be deprecated.

Answered   2023-09-20 20:25:54

  • @Wolfy87 Please be advised that there are some cases that typeof stringValue might return "object" instead of "string". See comments on my answer. - anyone
  • My preferred answer. The argument against it is that it 'fails' for object-wrapped strings like new String('foo'), but that doesn't matter because object-wrapped strings are a worthless feature that you shouldn't be using. The Google style guide forbids them, Douglas Crockford wants them deprecated, and no libraries use them. Pretend they don't exist, and use typeof without fear. - anyone
  • Didn't Douglas Crockford recommend that typeof be deprecated as well? - anyone
  • @DanielLe, because he proposed a replacement that fixes some issues, not because he's against it in principle. - anyone
  • If it causes you headaches, 99.99% of the time that's because you did not structure your code correctly. That's not NaN's fault for existing and doing what it does, that's something you should take note of, learn from, and bear in mind the next time you work with code that might yield it. - anyone
function isString(x) {
  return Object.prototype.toString.call(x) === "[object String]"
}

Or, inline (I have an UltiSnip setup for this):

Object.prototype.toString.call(myVar) === "[object String]"

FYI, Pablo Santa Cruz's answer is wrong, because typeof new String("string") is object

DRAX's answer is accurate and functional and should be the correct answer (since Pablo Santa Cruz is most definitely incorrect, and I won't argue against the popular vote.)

However, this answer is also definitely correct, and actually the best answer (except, perhaps, for the suggestion of using lodash/underscore). disclaimer: I contributed to the lodash 4 codebase.

My original answer (which obviously flew right over a lot of heads) follows:

I transcoded this from underscore.js:

['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'].forEach( 
    function(name) { 
        window['is' + name] = function(obj) {
              return toString.call(obj) == '[object ' + name + ']';
    }; 
});

That will define isString, isNumber, etc.


In Node.js, this can be implemented as a module:

module.exports = [
  'Arguments',
  'Function', 
  'String', 
  'Number', 
  'Date', 
  'RegExp'
].reduce( (obj, name) => {
  obj[ 'is' + name ] = x => toString.call(x) == '[object ' + name + ']';
  return obj;
}, {});

[edit]: Object.prototype.toString.call(x) works to delineate between functions and async functions as well:

const fn1 = () => new Promise((resolve, reject) => setTimeout(() => resolve({}), 1000))
const fn2 = async () => ({})

console.log('fn1', Object.prototype.toString.call(fn1))
console.log('fn2', Object.prototype.toString.call(fn2))

// Test this approach:

function isString(x) {
  return Object.prototype.toString.call(x) === "[object String]"
}

let falseCases = [
  [ 'null', null ],
  [ 'undefined', undefined ],
  [ 'object', { a: 1, b: 2 } ],
  [ 'array', [ 1, 2, 3 ] ],
  [ 'number', 123 ],
  [ 'zero', 0 ],
  [ 'RegExp', new RegExp('hello') ],
  [ 'number with valueOf returning string', Object.assign(10, { valueOf: () => 'abc' }) ],
  [ 'object pretending to be string', { constructor: String } ]
];
let trueCases = [
  [ 'empty literal string', '' ],
  [ 'unicode string literal', String.fromCharCode(10000) ],
  [ 'empty boxed string', new String('') ],
  [ 'unicode boxed string', new String(String.fromCharCode(10000)) ],
  [ 'string with overwritten "constructor"', Object.assign('hi', { constructor: Array }) ],
  [ 'string with overwritten "toString"', Object.assign('hi', { toString: 123 }) ],
  [ 'string with overwritten "valueOf"', Object.assign('hi', { valueOf: 123 }) ],
  [ 'string with overwritten "constructor"', Object.assign('hi', { constructor: RegExp }) ],
  [ 'proxied string', new Proxy(new String('hello'), {}) ],
];

console.log('NEGATIVE TESTS:');
for (let [ name, val ] of falseCases) {
  console.log(`Test ${name}:\n  Expect: false\n  Got:    ${isString(val)}`); 
}

console.log('\nPOSITIVE TESTS:');
for (let [ name, val ] of trueCases) {
  console.log(`Test ${name}:\n  Expect: true\n  Got:    ${isString(val)}`); 
}

Answered   2023-09-20 20:25:54

  • You recommend underscore.js (for what odd reason?) but you don't use it here. Moreover you pollute the global namespace with functions. In node.js you'd create a module that'd have all these functions (you can use global || window instead of window but that would be a bad approach to solve a problem you shouldn't have in the first place). - anyone
  • @BenjaminGruenbaum I came looking for the answer to the OP's question, and didn't like any of the answers. So I checked what underscore did, and thought it was nifty enough to extract and modify a little (to avoid having to have the underscore library loaded). I'll clarify my post. - anyone
  • @Orwellophile, How is this better than DRAX's answer? - anyone
  • JS supports monkey patching, so it's possible to re-define the toString in the Object.prototype. So, I'd argue that relying on toString to check an object's type is, at best, a bad practice. - anyone
  • i support having "wrong answer" and "shotgun-style answer" refer to post more specifically since the numbers of replies have aged, and then also explain why those answers are inferior as you present superior answer. my two-cents. - anyone

I recommend using the built-in functions from jQuery or lodash/Underscore. They're simpler to use and easier to read.

Either function will handle the case DRAX mentioned... that is, they both check if (A) the variable is a string literal or (B) it's an instance of the String object. In either case, these functions correctly identify the value as being a string.

lodash / Underscore.js

if(_.isString(myVar))
   //it's a string
else
   //it's something else

jQuery

if($.type(myVar) === "string")
   //it's a string
else
   //it's something else

See lodash Documentation for _.isString() for more details.

See jQuery Documentation for $.type() for more details.

Answered   2023-09-20 20:25:54

  • This is the essential of what is wrong with JS community - checking against primitive type is a one-liner and involves just language construction (one of the basic), but you recommend using external library. If someone already uses one of these libraries it might be a good idea, but downloading them just for that instead of simply checking the type is an overkill. - anyone
  • I'm going to agree with Rafal. I'm seeing everywhere that it improves "readability" to use one of these external libraries. If you know JavaScript, then that is easier to read than some external library you haven't used. _.every() is a little confusing to use at first, and something as simple as _.isBoolean() has confused devs at my company. A dev mistakenly thought it would be false if the value was a boolean and was false. English is easier to read than German for me, because I don't know German. Learn JavaScript and it will all make sense. - anyone
  • @RafałWrzeszcz These libraries are fairly widely used and provide much useful (and tested) functionality. Especially lodash. I wouldn't recommend someone download the library only to use for this one solution.... but I would recommend every javascript developer download this library and see what they are missing out on. ;) - anyone
  • All y'all are missing the point of a library like Lodash: not speed. Not "ease of development". The reason to use a library like Lodash provides "defensiveness" against issues that will blow up your js app. Fatal errors happen when you attempt to do string operations on an object (or vice versa), and Lodash provides tremendous value around preventing those errors. - anyone
  • All of these comments are valid but, man...only with JS would the suggestion of using a third-party library to check a type not get you laughed out of the dev room. - anyone

Edit: The current way to do it is typeof value === 'string'. For example:

const str = 'hello';
if (typeof str === 'string') { ... }

Below has been deprecated since node v4.

If you work on the node.js environment, you can simply use the built-in function isString in utils.

const util = require('util');
if (util.isString(myVar)) {}

Answered   2023-09-20 20:25:54

  • Is there any replacement? - anyone
  • Documents say "Use typeof value === 'string' instead." - anyone
  • x = new String('x'); x.isString(x); returns false. There is util.types.isStringObject() but that returns false for x = 'x' type string. Two utility functions that provide absolutely no utility... - anyone
function isString (obj) {
  return (Object.prototype.toString.call(obj) === '[object String]');
}

I saw that here:

http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/

Answered   2023-09-20 20:25:54

  • I think this solution is the most robust since it handles cross-frame/cross-window reference scenarios as mentioned in the URL provided in the answer. - anyone
  • Great answer, it looks like Underscore.js also uses this method! - anyone
  • @ling Just curious, why do you put parenthesis around Object.prototype.toString.call(obj) === '[object String]'? - anyone
  • How is that different from @Orwellophile's answer? - anyone
  • @JonathanH - if you look at the edit history of Orwellophile's answer, at the time this answer was written, Orwellophile said something quite complex. It was only in 2016 that that answer was edited to include this. So ling should get the credit! - anyone

Best way:

var s = 'String';
var a = [1,2,3];
var o = {key: 'val'};

(s.constructor === String) && console.log('its a string');
(a.constructor === Array) && console.log('its an array');
(o.constructor === Object) && console.log('its an object');
(o.constructor === Number || s.constructor === Boolean) && console.log('this won\'t run');

Each of these has been constructed by its appropriate class function, like "new Object()" etc.

Also, Duck-Typing: "If it looks like a duck, walks like a duck, and smells like a duck - it must be an Array" Meaning, check its properties.

Hope this helps.

Edit; 12/05/2016

Remember, you can always use combinations of approaches too. Here's an example of using an inline map of actions with typeof:

var type = { 'number': Math.sqrt.bind(Math), ... }[ typeof datum ];

Here's a more 'real world' example of using inline-maps:

function is(datum) {
    var isnt = !{ null: true, undefined: true, '': true, false: false, 0: false }[ datum ];
    return !isnt;
}
console.log( is(0), is(false), is(undefined), ... );  // >> true true false

This function would use [ custom ] "type-casting" -- rather, "type-/-value-mapping" -- to figure out if a variable actually "exists". Now you can split that nasty hair between null & 0!

Many times you don't even care about its type. Another way to circumvent typing is combining Duck-Type sets:

this.id = "998";  // use a number or a string-equivalent
function get(id) {
    if (!id || !id.toString) return;
    if (id.toString() === this.id.toString()) http( id || +this.id );
    // if (+id === +this.id) ...;
}

Both Number.prototype and String.prototype have a .toString() method. You just made sure that the string-equivalent of the number was the same, and then you made sure that you passed it into the http function as a Number. In other words, we didn't even care what its type was.

Hope that gives you more to work with :)

Answered   2023-09-20 20:25:54

  • You would need some other check for plain old numbers, since trying to take their constructor property will fail: - anyone
  • @torazaburo Worked fine for me just now in the Chrome console. What makes you think it won't work? - anyone
  • @torazaburo You may want to play with the assertions ( (o.constructor === Number || s.constructor === Boolean) ). Anecdotally, parseInt and NaN are fragile but powerful tools. Just remember, Not-a-Number is NOT Not-a-Number, and undefined can be defined. - anyone
  • a.constructor === Array is wrong and can fail sometimes, use Array.isArray see web.mit.edu/jwalden/www/isArray.html - anyone
  • Agreed, this isn't fail-safe. A better way is to use property checks -- THAT'S the only truly fail-safe way at the moment. Example: if(thing.call) { 'its a function'; } or if(thing.defineProperties) { 'its an object'; }. Thanks for the input, axkibe! - anyone

I can't honestly see why one would not simply use typeof in this case:

if (typeof str === 'string') {
  return 42;
}

Yes it will fail against object-wrapped strings (e.g. new String('foo')) but these are widely regarded as a bad practice and most modern development tools are likely to discourage their use. (If you see one, just fix it!)

The Object.prototype.toString trick is something that all front-end developers have been found guilty of doing one day in their careers but don't let it fool you by its polish of clever: it will break as soon as something monkey-patch the Object prototype:

const isString = thing => Object.prototype.toString.call(thing) === '[object String]';

console.log(isString('foo'));

Object.prototype.toString = () => 42;

console.log(isString('foo'));

Answered   2023-09-20 20:25:54

  • FWIW; Arguing against a solution because it could be broken by monkey-patching the Object prototype is a weak argument. In a dynamic language, almost anything can be broken by doing stuff you shouldn't do! - anyone
  • @ToolmakerSteve Fair. You are right of course. Someone could easily alter all native prototypes and nothing would work anymore. I guess the point I was trying to make is that in a (JS) world where monkey patching is still common practice, relying on such technique is fraught with danger and one shouldn't expose themselves to it when the (simpler) alternative is guaranteed to always work (AFAIK you cannot monkey patch typeof). Point taken nonetheless. Thank you. - anyone
  • Its a trade-off:, given that the two approaches don't always return the same answer. So it depends on your "spec" - what you mean by a string. "fraught with danger" seems a bit strong in this case. If someone modifies Object.prototype.toString such that it returns a different result... frankly that's their problem! IMHO the possibility shouldn't be a factor in deciding what approach to use. (I personally don't bother; I go with the simple approach you show - but then I'm not writing library code.) - anyone

Performance

Today 2020.09.17 I perform tests on MacOs HighSierra 10.13.6 on Chrome v85, Safari v13.1.2 and Firefox v80 for chosen solutions.

Results

For all browsers (and both test cases)

  • solutions typeof||instanceof (A, I) and x===x+'' (H) are fast/fastest
  • solution _.isString (lodash lib) is medium/fast
  • solutions B and K are slowest

enter image description here

Update: 2020.11.28 I update results for x=123 Chrome column - for solution I there was probably an error value before (=69M too low) - I use Chrome 86.0 to repeat tests.

Details

I perform 2 tests cases for solutions A B C D E F G H I J K L

  • when variable is string - you can run it HERE
  • when variable is NOT string - you can run it HERE

Below snippet presents differences between solutions

// https://stackoverflow.com/a/9436948/860099
function A(x) {
  return (typeof x == 'string') || (x instanceof String)
}

// https://stackoverflow.com/a/17772086/860099
function B(x) {
  return Object.prototype.toString.call(x) === "[object String]"
}

// https://stackoverflow.com/a/20958909/860099
function C(x) {
  return _.isString(x);
}

// https://stackoverflow.com/a/20958909/860099
function D(x) {
  return $.type(x) === "string";
}

// https://stackoverflow.com/a/16215800/860099
function E(x) {
  return x?.constructor === String;
}

// https://stackoverflow.com/a/42493631/860099
function F(x){
  return x?.charAt != null
}


// https://stackoverflow.com/a/57443488/860099
function G(x){
  return String(x) === x
}

// https://stackoverflow.com/a/19057360/860099
function H(x){
  return x === x + ''
}

// https://stackoverflow.com/a/4059166/860099
function I(x) {
  return typeof x == 'string'
}

// https://stackoverflow.com/a/28722301/860099
function J(x){
  return x === x?.toString()
}

// https://stackoverflow.com/a/58892465/860099
function K(x){
  return x && typeof x.valueOf() === "string"
}

// https://stackoverflow.com/a/9436948/860099
function L(x) {
  return x instanceof String
}

// ------------------
//     PRESENTATION
// ------------------

console.log('Solutions results for different inputs \n\n');
console.log("'abc' Str  ''  ' ' '1' '0'  1   0   {} [] true false null undef");

let tests = [ 'abc', new String("abc"),'',' ','1','0',1,0,{},[],true,false,null,undefined];

[A,B,C,D,E,F,G,H,I,J,K,L].map(f=> {  
console.log(
  `${f.name}   ` + tests.map(v=> (1*!!f(v)) ).join`   `
)})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script>


This shippet only presents functions used in performance tests - it not perform tests itself!

And here are example results for chrome

enter image description here

Answered   2023-09-20 20:25:54

  • Have you tried running your tests multiple times? I have strong doubt about strategy "i", running on Chrome with x = 123. You get 69M, though you get 671M for case A (which is essentially the same code, with an extra test). Here, that strategy wins in Chrome for x = 123. Not that important, honestly, but just a reminder that performance micro benchmarks are very difficult to get right. - anyone
  • yep - I run test multiple times in past - I also run It now - and you have right - now result for I is much better (I have 674M for "i") - I will update this (in free time) - thank you - anyone
  • @jwatkins - I update table with results - thanks for you comment :) - anyone
  • This is very, very useful - thanks! But some of the timed tests are arguably not correct - e.g. x + '' === x fails for strings created with new String("string"). Perhaps it should be limited to correct tests, or at least have added columns for the result of each test for a simple test suite of e.g. null, undefined, 123, new Object() (should all give false) and "", "abc", new String(""), new String("abc") (should all give true). - anyone
  • Test A seems to be getting a slight speedup (at least in Chrome on macOS) from using == instead of === - but not sure if this matters. - anyone

This is a great example of why performance matters:

Doing something as simple as a test for a string can be expensive if not done correctly.

For example, if I wanted to write a function to test if something is a string, I could do it in one of two ways:

1) const isString = str => (Object.prototype.toString.call(str) === '[object String]');

2) const isString = str => ((typeof str === 'string') || (str instanceof String));

Both of these are pretty straight forward, so what could possibly impact performance? Generally speaking, function calls can be expensive, especially if you don't know what's happening inside. In the first example, there is a function call to Object's toString method. In the second example, there are no function calls, as typeof and instanceof are operators. Operators are significantly faster than function calls.

When the performance is tested, example 1 is 79% slower than example 2!

See the tests: https://jsperf.com/isstringtype

Answered   2023-09-20 20:25:54

  • The test link is dead, but I believe you. This kind of information is super important. IMHO this should be, if not the most upvoted answer, at least the most upvoted comment on the current leading answer. - anyone
  • typeof str === 'string' || str instanceof String (can drop the parenthesis which I prefer in if (..) cases); regardless, checking both the primitive and object types in #2 is clear and sufficient. These checks should be 'rare' anyway. - anyone
  • here is a benchmark, 30x faster on firefox, 2 nanoseconds vs 50 ns - anyone
  • Yeah, @MilaNautikus the only issue with the Boolean(str.charCodeAt) solution is that it doesn't handle the case of undefined/null; otherwise I could have just said const isString = str => str.charCodeAt !== undefined for the same performance - anyone

I like to use this simple solution:

var myString = "test";
if(myString.constructor === String)
{
     //It's a string
}

Answered   2023-09-20 20:25:54

  • How is that different from Cody's answer, 4 years later? - anyone
  • @Sheljohn Cody's answer is great. My answer (complete text) is shorter and straight to the point. You asked... :) - anyone
  • As a function, this would need a way of dealing with undefined and null, and still getting the answer right for empty strings (both '' and new String('')). - anyone
  • @MikeBeaton No problem: (mystring || false) && mystring.constructor === String. I used false in case it's used in a function that must return a boolean. - anyone
  • @MikeBeaton - do empty strings return a different answer for .constructor? That would be quite surprising. - anyone

I find this simple technique useful to type-check for String -

String(x) === x // true, if x is a string
                // false in every other case

const test = x =>
  console.assert
    ( String(x) === x
    , `not a string: ${x}`
    )

test("some string")
test(123)           // assertion failed
test(0)             // assertion failed
test(/some regex/)  // assertion failed
test([ 5, 6 ])      // assertion failed
test({ a: 1 })      // assertion failed
test(x => x + 1)    // assertion failed

The same technique works for Number too -

Number(x) === x // true, if x is a number
                // false in every other case

const test = x =>
  console.assert
    ( Number(x) === x
    , `not a number: ${x}`
    )

test("some string") // assertion failed
test(123)           
test(0)             
test(/some regex/)  // assertion failed
test([ 5, 6 ])      // assertion failed
test({ a: 1 })      // assertion failed
test(x => x + 1)    // assertion failed

And for RegExp -

RegExp(x) === x // true, if x is a regexp
                // false in every other case

const test = x =>
  console.assert
    ( RegExp(x) === x
    , `not a regexp: ${x}`
    )

test("some string") // assertion failed
test(123)           // assertion failed
test(0)             // assertion failed
test(/some regex/)  
test([ 5, 6 ])      // assertion failed
test({ a: 1 })      // assertion failed
test(x => x + 1)    // assertion failed

Same for Object -

Object(x) === x // true, if x is an object
                // false in every other case

NB, regexps, arrays, and functions are considered objects too.

const test = x =>
  console.assert
    ( Object(x) === x
    , `not an object: ${x}`
    )

test("some string") // assertion failed
test(123)           // assertion failed
test(0)             // assertion failed
test(/some regex/)  
test([ 5, 6 ])      
test({ a: 1 })      
test(x => x + 1)    

But, checking for Array is a bit different -

Array.isArray(x) === x // true, if x is an array
                       // false in every other case

const test = x =>
  console.assert
    ( Array.isArray(x)
    , `not an array: ${x}`
    )

test("some string") // assertion failed
test(123)           // assertion failed
test(0)             // assertion failed
test(/some regex/)  // assertion failed
test([ 5, 6 ])      
test({ a: 1 })      // assertion failed
test(x => x + 1)    // assertion failed

This technique does not work for Functions however -

Function(x) === x // always false

For @Faither -

const fmt = JSON.stringify

function test1() {
  const a = "1"
  const b = 1
  console.log(`Number(${fmt(a)}) === ${fmt(b)}`, Number(a) === b) // true
}

function test2() {
  const a = "1"
  const b = 1
  console.log(`Number.isInteger(${fmt(a)})`, Number.isInteger(a)) // false
  console.log(`Number.isInteger(${fmt(b)})`, Number.isInteger(b)) // true
}

function test3() {
  name = 1 // global name will always be a string
  console.log(fmt(name)) // "1"
  console.log(`String(${fmt(name)}) === ${fmt(name)}`, String(name) === name) // true
}

function test4() {
  const name = 1 // local name 
  console.log(fmt(name)) // 1
  console.log(`String(${fmt(name)}) === ${fmt(name)}`, String(name) === name) // false
}

test1(); test2(); test3(); test4()

Answered   2023-09-20 20:25:54

  • var x = new String(x); String(x)===x returns false. however ({}).toString.call(x).search(/String/)>0 always returns for stringy things - anyone
  • function isClass(x,re){return ({}).toString.call(x).search(re)>0;}; isClass("hello",/String/) or isClass(3,/Number/) or isClass(null,/Null/) - anyone
  • This technique seems "non obvious" to me. Clever techniques that "work", but don't clearly express the intent, I find distasteful. - anyone
  • There is nothing clever about it. Constructors that receive an argument of the same type return the argument, unmodified. Maybe you’re simply unaware of this property? See also idempotence - anyone
  • @unsynchronized It's not obvious that new String(x) should count as a string, though. It's a wrapper object, with different behaviour to a normal string. Unless you for some weird reason have specific requirements about how you want your check to handle string wrapper objects (which you probably don't, because there's no reason to ever use them in the first place), it's not really a strike against this answer. - anyone
if (s && typeof s.valueOf() === "string") {
  // s is a string
}

Works for both string literals let s = 'blah' and for Object Strings let s = new String('blah')

Answered   2023-09-20 20:25:54

  • Attention! This will fail on empty strings, since those are falsey. - anyone

Taken from lodash:

function isString(val) {
   return typeof val === 'string' || ((!!val && typeof val === 'object') && Object.prototype.toString.call(val) === '[object String]');
}

console.log(isString('hello world!')); // true
console.log(isString(new String('hello world'))); // true

Answered   2023-09-20 20:25:54

You can use this function to determine the type of anything:

var type = function(obj) {
    return Object.prototype.toString.apply(obj).replace(/\[object (.+)\]/i, '$1').toLowerCase();
};

To check if a variable is a string:

type('my string') === 'string' //true
type(new String('my string')) === 'string' //true
type(`my string`) === 'string' //true
type(12345) === 'string' //false
type({}) === 'string' // false

https://codepen.io/patodiblasi/pen/NQXPwY?editors=0012

To check for other types:

type(null) //null
type(undefined) //undefined
type([]) //array
type({}) //object
type(function() {}) //function
type(123) //number
type(new Number(123)) //number
type(/some_regex/) //regexp
type(Symbol("foo")) //symbol

Answered   2023-09-20 20:25:54

  • This is a cute little function, although I wouldn't use it personally and would rather just do ad-hoc type checks as needed, like foo === null or typeof foo == "string". Downvotes might be because 1. this is maybe a bit non-idiomatic; although using Object.prototype.toString is common, I've never seen anyone pull the type out of the result like you do, only compare to exact values of possible results like "[object String]" 2. you don't explain what the regex does or why, and to JavaScript newbies this is likely very unclear, and 3. it's unclear why to prefer this over other answers. - anyone
  • A ridiculous counterexample is Object.assign('abc', { [Symbol.toStringTag]: 'notAString' }) - it's a string, but try passing it to type! - anyone

A simple and fast way to test can be using the constructor name attribute.

let x = "abc";
console.log(x.constructor.name === "String"); // true

let y = new String('abc');
console.log(y.constructor.name === "String"); // true

Performance

enter image description here

Answered   2023-09-20 20:25:54

  • your solution is the best here, and also works for other objects, as in the solution offered by @Orwellophile. - anyone
  • Best approach. Less work than having | clauses - anyone
  • Shorter would be x.constructor === String (and now it's also safe for cases where you have instances of some other class which also happens to be named String) - anyone

I also found that this works fine too, and its a lot shorter than the other examples.

if (myVar === myVar + '') {
   //its string
} else {
   //its something else
}

By concatenating on empty quotes it turns the value into a string. If myVar is already a string then the if statement is successful.

Answered   2023-09-20 20:25:54

  • The only problem being that you're coercing a variable when you want to check it's type. That seems a bit expensive to me when compared with typeof. - anyone
  • So yea, you're right. jsperf said it was around 20% slow than typeof but still quite a bit faster than toString. Either way, I guess I just like the syntax for coercing. - anyone
  • this does not work with the String type; var s = new String('abc'); > s === s + '' > false - anyone
  • Doesn't work with new String cus that creates a type of object. w3schools.com/js/tryit.asp?filename=tryjs_string_object2 - anyone
  • I find this approach distasteful. Writing good code isn't about making it shorter. Its about saying what you mean. - anyone
var a = new String('')
var b = ''
var c = []

function isString(x) {
  return x !== null && x !== undefined && x.constructor === String
}

console.log(isString(a))
console.log(isString(b))
console.log(isString(c))

Answered   2023-09-20 20:25:54

  • Why do you need to check for null or undefined if x.constructor === String would also return false for null or undefined? - anyone
  • @JulesManson: It would throw an error, not produce false. - anyone

The following method will check if any variable is a string (including variables that do not exist).

const is_string = value => {
  try {
    return typeof value() === 'string';
  } catch (error) {
    return false;
  }
};

let example = 'Hello, world!';

console.log(is_string(() => example)); // true
console.log(is_string(() => variable_doesnt_exist)); // false

Answered   2023-09-20 20:25:54

  • -1; the interface here is just weird. From the name I expect is_string(x) to tell me whether x is a string, but instead it tells me whether x is a callable that returns a string. Why would I want to pass in a function instead of passing my value directly? - anyone
  • @MarkAmery This is_string function is for the purposes of checking if a variable exists and is a string. The arrow function being passed allows one to pass a variable that does not exist, whereas, normally, we would receive the error: "Uncaught ReferenceError: variable is not defined" if the variable didn't exist. The use case is similar to the Error Control Operator in PHP (i.e., is_string(@$example)). It may not the best or most common practice, but someone may find it useful, and that's what makes this answer unique from the others. - anyone

This is good enough for me.

WARNING: This is not a perfect solution. See the bottom of my post.

Object.prototype.isString = function() { return false; };
String.prototype.isString = function() { return true; };

var isString = function(a) {
  return (a !== null) && (a !== undefined) && a.isString();
};

And you can use this like below.

//return false
isString(null);
isString(void 0);
isString(-123);
isString(0);
isString(true);
isString(false);
isString([]);
isString({});
isString(function() {});
isString(0/0);

//return true
isString("");
isString(new String("ABC"));

WARNING: This works incorrectly in the case:

//this is not a string
var obj = {
    //but returns true lol
    isString: function(){ return true; }
}

isString(obj) //should be false, but true

Answered   2023-09-20 20:25:54

A simple solution would be:

var x = "hello"

if(x === x.toString()){
// it's a string 
}else{
// it isn't
}

Answered   2023-09-20 20:25:54

  • this doesn't checks if it's a string. It makes into a string, lots of things have toString() function - anyone
  • @MuhammadUmer Yes, it converts it into a string but then checks for identity against the original value, which will only be True if the original value is also a string. - anyone
  • this is wrong: you can't blindly call .toString on any values; try if the x to be checked is null or undefined, your code throw exception - anyone
  • The idea is still usable. x === String(x) is safe and works. - anyone
  • Really? This solution seems too weird for me, because toString() method may be overridden and may throw an exception (due to some specific implementation), and your check will not work for sure. Main idea is that you shouldn't call methods that are not related to what you want to get. I'm not even talking about unnecessary overhead related to the toString method. Downvoting. - anyone

A Typechecker helper:

function isFromType(variable, type){
  if (typeof type == 'string') res = (typeof variable == type.toLowerCase())
  else res = (variable.constructor == type)
  return res
}

usage:

isFromType('cs', 'string') //true
isFromType('cs', String) //true
isFromType(['cs'], Array) //true
isFromType(['cs'], 'object') //false

Also if you want it to be recursive(like Array that is an Object), you can use instanceof.

(['cs'] instanceof Object //true)

Answered   2023-09-20 20:25:54

I'm going to go a different route to the rest here, which try to tell if a variable is a specific, or a member of a specific set, of types.
JS is built on ducktyping; if something quacks like a string, we can and should use it like a string.

Is 7 a string? Then why does /\d/.test(7) work?
Is {toString:()=>('hello there')} a string? Then why does ({toString:()=>('hello there')}) + '\ngeneral kenobi!' work?
These aren't questions about should the above work, the point is they do.

So I made a duckyString() function
Below I test many cases not catered for by other answers. For each the code:

  • sets a string-like variable
  • runs an identical string operation on it and a real string to compare outputs (proving they can be treated like strings)
  • converts the string-like to a real string to show you duckyString() to normalise inputs for code that expects real strings
text = 'hello there';
out(text.replace(/e/g, 'E') + ' ' + 'hello there'.replace(/e/g, 'E'));
out('Is string? ' + duckyString(text) + '\t"' + duckyString(text, true) + '"\n');

text = new String('oh my');
out(text.toUpperCase() + ' ' + 'oh my'.toUpperCase());
out('Is string? ' + duckyString(text) + '\t"' + duckyString(text, true) + '"\n');

text = 368;
out((text + ' is a big number') + ' ' + ('368' + ' is a big number'));
out('Is string? ' + duckyString(text) + '\t"' + duckyString(text, true) + '"\n');

text = ['\uD83D', '\uDE07'];
out(text[1].charCodeAt(0) + ' ' + '😇'[1].charCodeAt(0));
out('Is string? ' + duckyString(text) + '\t"' + duckyString(text, true) + '"\n');

function Text() { this.math = 7; }; Text.prototype = {toString:function() { return this.math + 3 + ''; }}
text = new Text();
out(String.prototype.match.call(text, '0') + ' ' + text.toString().match('0'));
out('Is string? ' + duckyString(text) + '\t"' + duckyString(text, true) + '"\n');

This is in the same vein as !!x as opposed to x===true and testing if something is array-like instead of necessitating an actual array.
jQuery objects; are they arrays? No. Are they good enough? Yeah, you can run them through Array.prototype functions just fine.
It's this flexibility that gives JS its power, and testing for strings specifically makes your code less interoperable.

The output of the above is:

hEllo thErE hEllo thErE
Is string? true "hello there"

OH MY OH MY
Is string? true "oh my"

368 is a big number 368 is a big number
Is string? true "368"

56839 56839
Is string? true "😇"

0 0
Is string? true "10"

So, it's all about why you want to know if something's a string.
If, like me, you arrived here from google and wanted to see if something was string-like, here's an answer.
It isn't even expensive unless you're working with really long or deeply nested char arrays.
This is because it is all if statements, no function calls like .toString().
Except if you're trying to see if a char array with objects that only have toString()'s or multi-byte characters, in which case there's no other way to check except to make the string, and count characters the bytes make up, respectively

function duckyString(string, normalise, unacceptable) {
    var type = null;
    if (!unacceptable)
        unacceptable = {};
    if (string && !unacceptable.chars && unacceptable.to == null)
        unacceptable.to = string.toString == Array.prototype.toString;

    if (string == null)
        ;

    //tests if `string` just is a string
    else if (
        !unacceptable.is &&
        (typeof string == 'string' || string instanceof String)
    )
        type = 'is';

    //tests if `string + ''` or `/./.test(string)` is valid
    else if (
        !unacceptable.to &&
        string.toString && typeof string.toString == 'function' && string.toString != Object.prototype.toString
    )
        type = 'to';

    //tests if `[...string]` is valid
    else if (
        !unacceptable.chars &&
        (string.length > 0 || string.length == 0)
    ) {
        type = 'chars';
        //for each char
        for (var index = 0; type && index < string.length; ++index) {
            var char = string[index];

            //efficiently get its length
            var length = ((duckyString(char, false, {to:true})) ?
                char :
                duckyString(char, true) || {}
            ).length;

            if (length == 1)
                continue;

            //unicode surrogate-pair support
            char = duckyString(char, true);
            length = String.prototype[Symbol && Symbol.iterator];
            if (!(length = length && length.call(char)) || length.next().done || !length.next().done)
                type = null;
        }
    }

    //return true or false if they dont want to auto-convert to real string
    if (!(type && normalise))
        //return truthy or falsy with <type>/null if they want why it's true
        return (normalise == null) ? type != null : type;

    //perform conversion
    switch (type) {
    case 'is':
        return string;
    case 'to':
        return string.toString();
    case 'chars':
        return Array.from(string).join('');
    }
}

Included are options to

  • ask which method deemed it string-y
  • exclude methods of string-detection (eg if you dont like .toString())

Here are more tests because I'm a completionist:

out('Edge-case testing')
function test(text, options) {
    var result = duckyString(text, false, options);
    text = duckyString(text, true, options);
    out(result + ' ' + ((result) ? '"' + text + '"' : text));
}
test('');
test(null);
test(undefined);
test(0);
test({length:0});
test({'0':'!', length:'1'});
test({});
test(window);
test(false);
test(['hi']);
test(['\uD83D\uDE07']);
test([['1'], 2, new String(3)]);
test([['1'], 2, new String(3)], {chars:true});
  • All negative cases seem to be accounted for
  • This should run on browsers >= IE8
  • Char arrays with multiple bytes supported on browsers with string iterator support

Output:

Edge-case testing
is ""
null null
null null
to "0"
chars ""
chars "!"
null null
chars ""
to "false"
null null
chars "😇"
chars "123"
to "1,2,3"

Answered   2023-09-20 20:25:54

Implementation from lodash library v4.0.0

// getTag.js

const toString = Object.prototype.toString;

/**
 * Gets the `toStringTag` of `value`.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
function getTag(value) {
    if (value == null) {
        return value === undefined 
            ? "[object Undefined]" 
            : "[object Null]";
    }
    return toString.call(value);
}
// isString.js

import getTag from "./getTag.js";

/**
 * Checks if `value` is classified as a `String` primitive or object.
 *
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a string, else `false`.
 * @example
 *
 * isString('abc')
 * // => true
 *
 * isString(1)
 * // => false
 */
function isString(value) {
    const type = typeof value;
    return (
        type === "string" || (type === "object" &&
                              value != null &&
                              !Array.isArray(value) &&
                              getTag(value) == "[object String]")
    );
}

export default isString;

Answered   2023-09-20 20:25:54

isString() checks whether the passed argument is a string or not, using optional chaining and the latest standards:

const isString = (value) => { 
    return value?.constructor === String;
}

Answered   2023-09-20 20:25:54

Just to expand on @DRAX's answer, I'd do this:

function isWhitespaceEmptyString(str)
{
    //RETURN:
    //      = 'true' if 'str' is empty string, null, undefined, or consists of white-spaces only
    return str ? !(/\S/.test(str)) : (str === "" || str === null || str === undefined);
}

It will account also for nulls and undefined types, and it will take care of non-string types, such as 0.

Answered   2023-09-20 20:25:54

I have a technique that's stupid. But straightforward.

if(maybeAString.toUpperCase)
  weHaveAString(maybeAString)

Yeah, it's far from perfect. But it is straightforward.

Answered   2023-09-20 20:25:54

  • @Mike why would it throw an error? if there is no "toUpperCase" member then that would resolve to undefined which would fail the condition test like expected, without throwning any exception whatsoever. - anyone
  • @andreyrk Did you even try it before you commented? Paste this into your JS console: let x = 123; console.log(x.toUpperCase()); - anyone
  • @Mike Reread the answer and check if your code matches. Hint: toUpperCase is not the same as toUpperCase() - anyone
  • @andreyrk Ah, you're right. I misread it. - anyone
  • Awesome! `const isString = x => !!x.toUpperCase - anyone

A code to have only string without any numbers

isNaN("A") = true;
parseInt("A") = NaN;
isNaN(NaN) = true;

Than we can use isNaN(parseInt()) to have only the string

let ignoreNumbers = "ad123a4m";

let ign = ignoreNumbers.split("").map((ele) => isNaN(parseInt(ele)) ? ele : "").join("");

console.log(ign);

Answered   2023-09-20 20:25:54

This function is a safe way to check for any type:

let isType = (value, type) => {
    if (type == null || value == null) return value === type;
    return Object.getPrototypeOf(value ?? {}).constructor === type;
}

// All the following work as expected:
isType('abc', String);
isType(123, Number);
isType(/abc/, RegExp);
isType(null, null);
isType(undefined, undefined);

From this we can derive:

let isString = value => isType(value, String);

// Test this approach:

let isType = (value, type) => {
    if (type == null || value == null) return value === type;
    return Object.getPrototypeOf(value ?? {}).constructor === type;
}
let isString = value => isType(value, String);

let falseCases = [
  [ 'null', null ],
  [ 'undefined', undefined ],
  [ 'object', { a: 1, b: 2 } ],
  [ 'array', [ 1, 2, 3 ] ],
  [ 'number', 123 ],
  [ 'zero', 0 ],
  [ 'RegExp', new RegExp('hello') ],
  [ 'number with valueOf returning string', Object.assign(10, { valueOf: () => 'abc' }) ],
  [ 'object pretending to be string', { constructor: String } ]
];
let trueCases = [
  [ 'empty literal string', '' ],
  [ 'unicode string literal', String.fromCharCode(10000) ],
  [ 'empty boxed string', new String('') ],
  [ 'unicode boxed string', new String(String.fromCharCode(10000)) ],
  [ 'string with overwritten "constructor"', Object.assign('hi', { constructor: Array }) ],
  [ 'string with overwritten "toString"', Object.assign('hi', { toString: 123 }) ],
  [ 'string with overwritten "valueOf"', Object.assign('hi', { valueOf: 123 }) ],
  [ 'string with overwritten "constructor"', Object.assign('hi', { constructor: RegExp }) ],
  [ 'proxied string', new Proxy(new String('hello'), {}) ],
];

console.log('NEGATIVE TESTS:');
for (let [ name, val ] of falseCases) {
  console.log(`Test ${name}:\n  Expect: false\n  Got:    ${isString(val)}`); 
}

console.log('\nPOSITIVE TESTS:');
for (let [ name, val ] of trueCases) {
  console.log(`Test ${name}:\n  Expect: true\n  Got:    ${isString(val)}`); 
}

Answered   2023-09-20 20:25:54

We also can use isFinite() rather than typeof or isNAN(). Check this:

var name="somename",trickyName="123", invalidName="123abc";
typeof name == typeof trickyName == typeof invalidName == "string" 🤷‍♀️

isNAN(name)==true
isNAN(trickyName)==false
isNAN(invalidName)==true 👀

where:

isFinite(name) == false
isFinite(trickyName)== true
isFinite(invalidName)== true

So we can do:

if(!isFinite(/*any string*/))
  console.log("it is string type for sure")

Notice that:

    isFinite("asd123")==false
    isNAN("asd123")==true

Answered   2023-09-20 20:25:54