Check if a variable is a string in JavaScript
Asked Answered
M

33

2785

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

Milch answered 30/10, 2010 at 14:36 Comment(0)
B
2407

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.
Burney answered 30/10, 2010 at 14:40 Comment(12)
@Wolfy87 Please be advised that there are some cases that typeof stringValue might return "object" instead of "string". See comments on my answer.Contribute
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.Inainability
Didn't Douglas Crockford recommend that typeof be deprecated as well?Jarrett
@DanielLe, because he proposed a replacement that fixes some issues, not because he's against it in principle.Goulette
@VsevolodGolovanov How do you know that he's not against it in principle? All I know is those issues are precisely why he recommended that the current implementation of typeof be deprecated.Jarrett
let value = NaN; typeof value => 'number' JS sucks completely.Operant
@MarkAmery what's the use case for declaring a string that way? Seems like bad practice.Paralysis
@MarekMarczak Not sure I understand: NaN is a special IEEE floating point value, and should absolutely be considered a number in every programming language that uses IEEE floats, with a value that indicates it will yield undefined behaviour if you plug it into further maths statements. Just like Infinity.Scatology
@Mike'Pomax'Kamermans maybe NaN type could be useful in scientific calculations and represents infinity but in everyday programming it causes a headache.Operant
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.Scatology
If you, guys, look in the 'path' module code of NodeJS you will see there just if (typeof path !== 'string')[1][2]. Just imagine what a huge number of users of this module is and no one complains about the problem with the object wrappers!Strati
This is a tricky question that has been already answered here: #50082812Madelle
C
2912

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)}`); 
}
Contribute answered 24/2, 2012 at 19:38 Comment(44)
Does "myVar instanceof String" do anything above and beyond "typeof myVar == 'string'" ?Hovis
If I am not wrong, typeof returns name of type as string ("string", "integer" ...) while instanceof compares actual types. I can't remember right now, but I think that in some cases typeof can return something other than "string" and thats why is double checked.Contribute
@Hovis 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.Contribute
var somevar = new String('somestring') console.log(typeof somevar) // objectAltonaltona
Just tested this: alert(new String() instanceof String); returns true in FireFox (while for example alert(new Object() instanceof String); returns false).Contribute
-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).Inainability
Also, if you're for some crazy reason using object-wrapped strings in your code, it's worth noting that the function here won't work with cross-frame code; it won't recognise object-wrapped strings that have been postMessaged from another frame because their constructor will be the String global from the other frame. This is an improbable hypothetical situation, but since you're already adding complexity to your solution to handle the improbable hypothetical situation of people using object-wrapped strings at all, I feel justified in pointing it out.Inainability
Indeed for general usage, it should be used primitive string. However there are some cases where using instances of String is more useful. For example, performance critical code could actually execute faster using String than primitive. pastebin.com/KAwht0epContribute
To sum up, I wouldn't call primitive wrappers "worthless feature", and code provided is not unusual at all. They have their use cases.Contribute
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!Evieevil
That said, I may like @Ling's answer even more, as it handles MarkAmery's issue, too...Evieevil
Perhaps Ling's answer is neater, but there are 2 things that you need to keep in mind: 1. Someone could modify Object.prototype.toString method and break code (which is highly unlikely to happen, but still...) 2. It is much slower to execute.Contribute
I think you can replace that condition by myVar.constructor === String, it works for both cases: myVar = "Foobar" or myVar = new String("Foobar")Curlicue
That is interesting trick, but it will break if someone create String instance and change constructor and vice-versa (create instance of anything but String and then sets constructor to String). Both cases are unlikely to happen, but I prefer to create robust code. Also you will need to check if myVar is null as well.Contribute
@MichaelTheriot the question here isn't just whether there are imaginable use-cases where the instanceof check could be useful (surely there are, if you're sufficiently creative, and also ones where it's harmful), it's whether it's more or less correct to use it than to omit it. The question asks how to check if a variable is a string. Is an object-wrapped String a string? Fuck if I know. It's unclear what, semantically, such a construct should be considered to represent, which is why they shouldn't be in the language in the first place and the style guides advise never to use them.Inainability
@MarkAmery Have you thought about editing this answer to include the information from your comment? Essentially it could say to just use typeof...unless you want to guard against those particularly bad coding practices, which almost nobody uses and nobody should, in which case you could include the instanceof check? Maybe either you or the OP could consider adding this information?Rizika
@MarkAmery There are several unconventional benefits of using string objects. If you are not performing strict equality / dropping them into a switch statement (property lookup is faster) I can't see a reason to exclude them.Crotch
I could agree that this is checking for "noise", but this is the correct answer. What gets me is that SO doesn't move this to the top as it clearly has more upvotes.Cyndi
Is var obj = new String("some value") really a string? console.log(obj) shows more like a char array. Strings are instantiated with String("some value") (without new keyword) and typeof String(something) evaluates to "string".Novelize
@Novelize They are not instantiated without new keyword. Using String(someValue) is used for converting value (eg. number) to string, not for creating.Contribute
@DanubianSailor thats a great example, its missing a semicolon. Interesting if the new keyword is omitted you get a 'string' : var Str = String(''); console.log(typeof Str, Str instanceof String); // string falseForeshank
if(str.trim) {} is a very simple hack that works in all scenarios. checking for the existance of trim, a method only part of the string prototypeGanef
let a:any = 'some string'; console.log(a instanceof String) results in false on my machine.Jowett
Google. Crockford. Ok, well, whatever. This answer uses basic syntax and operators of the JavaScript language. It shows true understanding of the fundamental situation at hand. Winner.Detradetract
...also if !!('some string', String) which return true.Banshee
@darcher: Unless I'm missing something, ('some string', String) will always return String, which will always be truthy regardless of the type of the first argument.Keeley
@Keeley you're very right, was probably tipsy when I posted this and later found the error in my ways.Banshee
@Ganef This doesn't work in all scenarios. It's perfectly valid for a non-string to have a method or property called trim.Keeley
@Keeley Maybe it is not futureproof; if Array ever got a trim method, it would no longer work when comparing array to string. But it is still the shortest way to get a truthy value for checking for a string.Ganef
@Ganef I think you're only considering built-in types. Any [proto]type defined by you or a library could have a trim method or property, as well as any arbitrary object.Keeley
@DRAX: Is that the benchmark code you used to come to that conclusion? Because it sorts arrPrim twice instead of arrPrim then arrObj, uses getMilliseconds instead of getTime, and only runs once. After correcting these, primitives are consistently faster, as expected. Primitive wrappers are indeed worthless.Knot
@Knot I can't remember, it was long time ago. But you are right that there is error in that code (arrPrim being sorted twice) and performance is slower when corrected. Thank you for correcting me. But I wouldn't say that wrappers are worthless. Here is one case that makes it perform better: jsbench.me/a4k2n4yynx Basically JS engines don't have shortcuts for custom methods, and they need to do wrapping of primitive to String object.Contribute
@DRAX: Side effect of missing strict mode and extending native prototypes. If you either use a regular function or add 'use strict' inside the function, the primitive version will be faster (300x).Knot
@Knot You are correct. Here is updated code: jsbench.me/tuk2n5xsuzContribute
"somestring" instanceof String resolves to falseFilar
@Filar that is correct, and new String("somestring") instanceof String resolves to trueContribute
@Filar - correct, which is why Drax has two different tests. The first one is for primitive strings typeof "somestring" === string, the second test is for String objects ``new String("somestring" instanceof String.Testudo
@ruffin the prose was pretty confusing and some of the claims made strike me as just plain wrong. I can see arguing that "foo" and new String("foo") are both strings (though it's not obvious to me as a point of terminology whether String objects wrapping strings should count as "strings" are not), but they're definitely not "the same" as the edit states - they have important behavioural differences (e.g. https://mcmap.net/q/40838/-what-is-the-difference-between-string-primitives-and-string-objects-in-javascript), and new String("foo") is an instance of String, not "a class of String".Inainability
@ruffin I don't object at all to some explanation being added, but such an edit should explain 1. that new String("foo") and "foo" evaluate to values with different types with differences in how they behave (which go beyond how they show up to typeof and instanceof checks), 2. that the typeof check lets you detect the non-object-wrapped strings while the instanceof check lets you detect the object-wrapped ones. IMO the edit I reverted wasn't clear about the second point and seemed to actively contradict the first one, so I think it did more harm than good.Inainability
@MarkAmery Thanks for the clarification; makes perfect sense. (I'd asked why he'd reverted an edit where someone (awkwardly) tried to add the content from his comments here to the answer).Driskill
For whatever reason I had to declare a variable like this. var mytype = typeof myobj; if (mytype == "string") .....Danialdaniala
This is superficial at the best, and fundamentally complicated. An instance of Object is not a data type. It may be obvious, may be "Nah! JS auto-boxes it and you can work with both interchangeably in real-world", but that fact brings some fundamental points. It works for me, it works for you, it works for 95% of the cases, until it doesn't and we have a hard bug to debug. For example: a is String, b is string. Unexpected outputs can occur, having typeof a == 'object', a instanceof Object (true, true) and this solution, especially if it's buried in a deeply nested node_modules lib.Veranda
If negating it seems instanceof should come first: if (!(myVar instanceof String) || typeof myVar !== 'string') { // It's something else }Misbecome
Never use instaceof for this purpose. JSON.stringify({}) instanceof String guess what? it's false. But typeof JSON.stringify({}) === 'string' is trueItalic
B
2407

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.
Burney answered 30/10, 2010 at 14:40 Comment(12)
@Wolfy87 Please be advised that there are some cases that typeof stringValue might return "object" instead of "string". See comments on my answer.Contribute
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.Inainability
Didn't Douglas Crockford recommend that typeof be deprecated as well?Jarrett
@DanielLe, because he proposed a replacement that fixes some issues, not because he's against it in principle.Goulette
@VsevolodGolovanov How do you know that he's not against it in principle? All I know is those issues are precisely why he recommended that the current implementation of typeof be deprecated.Jarrett
let value = NaN; typeof value => 'number' JS sucks completely.Operant
@MarkAmery what's the use case for declaring a string that way? Seems like bad practice.Paralysis
@MarekMarczak Not sure I understand: NaN is a special IEEE floating point value, and should absolutely be considered a number in every programming language that uses IEEE floats, with a value that indicates it will yield undefined behaviour if you plug it into further maths statements. Just like Infinity.Scatology
@Mike'Pomax'Kamermans maybe NaN type could be useful in scientific calculations and represents infinity but in everyday programming it causes a headache.Operant
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.Scatology
If you, guys, look in the 'path' module code of NodeJS you will see there just if (typeof path !== 'string')[1][2]. Just imagine what a huge number of users of this module is and no one complains about the problem with the object wrappers!Strati
This is a tricky question that has been already answered here: #50082812Madelle
V
259
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)}`); 
}
Verst answered 21/7, 2013 at 11:59 Comment(24)
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).Phillida
@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.Verst
@Verst Cool, I get it now, your original answer was phrased like you're suggesting underscore itself. Personally I'd just check myObject+"" === myObject to check if an object is a string (or even better, I wouldn't type check in a behavior driven type system in the first place).Phillida
@BenjaminGruenbaum well, I guess I am suggesting underscore :) I find that when I use my own methods (like that one you just posted), I tend to use different checks in different pieces of code. Eventually I grow paranoid that a particular variant may be incorrect, and I end up double checking, rewriting, etc. So for me, a definitive isWhatever() means that I can be consistent, and if it turns out to ever be wrong, I can change it one place only. Also, I can create matching functions in other languages (I'm doing BASH now) and have a consistent naming convention.Verst
@BenjaminGruenbaum myObject+"" === myObject won't work in the case myObject = new String("string"). you have to use myObject instanceof String. See DRAX's answerMedovich
@Medovich in that case it's not the same, a primitive and a wrapped value are not the same. A String object has a completely different behavior from a string primitive value type (try adding a propert for example). DRAX's answer is wrong in that regard imo.Phillida
DRAX's answer may be wrong only in prototypal inheritance context, depending on what you're trying to achieve. In others cases, it is correctMedovich
Cool solution for people with systemwise architecture concerns.Selene
FYI. Wrong for NaN. Check. toString.call(1-"a") => "[object Number]". But actually 1-"a" ==> NaNOla
@Ola NaN is still an instance of Number, specifically Number.NaN. typeof NaN also returns "number", in case anybody else is wondering. Either way, it's not a String, which is what this question is about.Verst
Interestingly, Array.isArray is becoming a standard function with a recommended shim implementation identical to Object.prototype.toString.call(arg) == '[object Array]'. I don't see any reason why an identical method shouldn't exist as String.isString = function(arg){return Object.prototype.toString.call(arg) == '[object String]';} It just works. I love the iteration to define these for other classes like Function, Number, etc.... but why pollute the global namespace? Why not follow the lead of Array.isArray add them as 'static' methods on their corresponding types?Scabby
Indeed, that's just what appealed to me. I have an editor (VIM) macro that expands isAnything to Object.prototype.toString.call(arg) == '[object ${Anything}] so I am always covered, even if I need something rare like isArgumentsVerst
@Orwellophile, How is this better than DRAX's answer?Compulsory
Is the rendering of toString for objects standardised? Or could a future standards-compliant browser implement Object.toString as rendering "[Dude its an Object!]"?Fleecy
@Fleecy Yes. Note also that because we use toString.call(), this cannot be exploited with something like x = { toString: () => '[object Number]' }.Argos
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.Analisaanalise
This answer looks complete, but I am curious about performance of this versus typeof.Biotin
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.Bathesda
It’s not a popularity contest. People will use which ever answer suits them best. I liked my answer because it shows by example that the method used for checking type is applicable to many object types. I like methods that are consistent, and I like the DRY principle. I also like that the answer is from one of the most popular JS libraries, and has been tested on every conceivable platform and on thousands of really big sites. Adding extra functions to Objects is just bad form. I’m not forcing anyone to define global functions either, just presenting a simpler version of lodash code.Verst
-1 because this doesn't really add anything compared to DRAX's answer, and because it characterises Pablo's answer as wrong for not treating new String("string") as a string without providing any argument for why it should be treated as a string. (IMO, it shouldn't be. Object-wrapped strings are not the same thing as strings and behave differently. TypeScript agrees with me, for what it's worth; const foo: string = new String("string") is a compilation error.)Inainability
thank you @Verst for giving an answer that not only laconically answers the narrow question, but also enriches the readers' knowledge and preparedness for similar problems.Veneering
@MarkAmery I didn't make the rules Mark, if toString.call(new String("string")) == '[object String]' then I'm afraid it's a string. Also, (new String('test')).toString() === 'test'. It's your right to disagree.Verst
This is the best answer and regret to say some of the commentators seem to not read English too well. @Verst did NOT recommend underscore, but rather he referred to it as his source of wisdom. Also, arguments like "you can redefine toString" (@Andre Rodrigues) deserve replies like "Get your **** together." Your code, you should know what is being redefined, and calling bad practice on the same breath as suggesting redefining standard methods is precious. As for future possible incompatibilities, welcome to the World.Martineau
@Martineau Looks like you misunderstood me, which is kind of ironic.I didn’t recommend redefining the toString method, I said it’s a possibility. So if that happens (and it can happen outside your code base), then this solution might break. It’s a long shot, I’ll give you that, but just throwing it ou there.Analisaanalise
P
96

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.

Phyl answered 6/1, 2014 at 20:43 Comment(11)
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.Hid
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.Douse
@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. ;)Phyl
In production, you'd want things to work as fast as possible and while a library significantly cuts down development time, it could increase application complexity by adding a layer of abstraction. Every developer has to study the library to use it instead of a "simple" language construct. The takeaway is this: If you're going to use a library for your application, only add it if you know you'll be doing more than two or three one-liners or "checks" of this sort :)Mechanician
You should never use 3rd party libraries if not really needed!Curran
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.Rodneyrodolfo
Bear in mind that many people will be doing this in a Node or Node-like environment, and very few people will be using jQuery there.Sweetscented
For client side Apps, the size issue can be mitigated through proper bundling / tree shaking which you should have in place anyway. Also, I haven't seen any normal sized project without lodash or a similar lib. See my answer below.Abib
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.Felspar
If I am already using lodash (or equivalent) I would not mind using library capabilities to get my work done. I would however not prefer to use it solely for one operationEbonyeboracum
I would recommend against this solution even if you already use jQuery or lodash; if you always rely on a third-parties for such basic things you will never master the programming language. You’ll be a lot more productive learning how JS works instead of relying on these.Carreno
V
65

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)) {}
Vtol answered 13/11, 2016 at 1:40 Comment(3)
Is there any replacement?Karlie
Documents say "Use typeof value === 'string' instead."Kopple
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...Aitch
A
49
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/

Ambrosane answered 24/7, 2014 at 19:7 Comment(8)
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.Wheeling
Great answer, it looks like Underscore.js also uses this method!Fratricide
@Ambrosane Just curious, why do you put parenthesis around Object.prototype.toString.call(obj) === '[object String]'?Parotid
@Earlee You mean (x === y) has better readability than x === y?Parotid
@Parotid In my opinion, yes. It's also about consistency. I personally always use parentheses when returning a value.Simoom
How is that different from @Orwellophile's answer?Argos
@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!Testudo
IMHO the useless use of parentheses hurts visibility here: do you also do var a = ((1) + (1))? return is a statement, not a function call.Carreno
K
39

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 :)

Kinase answered 25/4, 2013 at 13:15 Comment(5)
You would need some other check for plain old numbers, since trying to take their constructor property will fail:Lederer
@torazaburo Worked fine for me just now in the Chrome console. What makes you think it won't work?Inainability
@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.Kinase
a.constructor === Array is wrong and can fail sometimes, use Array.isArray see web.mit.edu/jwalden/www/isArray.htmlAmaty
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!Kinase
U
39

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'));
Urochrome answered 18/1, 2019 at 23:22 Comment(3)
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!Testudo
@Testudo 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.Urochrome
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.)Testudo
U
32

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://mcmap.net/q/40453/-check-if-a-variable-is-a-string-in-javascript
function A(x) {
  return (typeof x == 'string') || (x instanceof String)
}

// https://mcmap.net/q/40453/-check-if-a-variable-is-a-string-in-javascript
function B(x) {
  return Object.prototype.toString.call(x) === "[object String]"
}

// https://mcmap.net/q/40453/-check-if-a-variable-is-a-string-in-javascript
function C(x) {
  return _.isString(x);
}

// https://mcmap.net/q/40453/-check-if-a-variable-is-a-string-in-javascript
function D(x) {
  return $.type(x) === "string";
}

// https://mcmap.net/q/40453/-check-if-a-variable-is-a-string-in-javascript
function E(x) {
  return x?.constructor === String;
}

// https://mcmap.net/q/40453/-check-if-a-variable-is-a-string-in-javascript
function F(x){
  return x?.charAt != null
}


// https://mcmap.net/q/40453/-check-if-a-variable-is-a-string-in-javascript
function G(x){
  return String(x) === x
}

// https://mcmap.net/q/40453/-check-if-a-variable-is-a-string-in-javascript
function H(x){
  return x === x + ''
}

// https://mcmap.net/q/40453/-check-if-a-variable-is-a-string-in-javascript
function I(x) {
  return typeof x == 'string'
}

// https://mcmap.net/q/40453/-check-if-a-variable-is-a-string-in-javascript
function J(x){
  return x === x?.toString()
}

// https://mcmap.net/q/40453/-check-if-a-variable-is-a-string-in-javascript
function K(x){
  return x && typeof x.valueOf() === "string"
}

// https://mcmap.net/q/40453/-check-if-a-variable-is-a-string-in-javascript
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

Uhlan answered 17/9, 2020 at 20:44 Comment(7)
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.Generic
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 youKurbash
@jwatkins - I update table with results - thanks for you comment :)Kurbash
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).Hyohyoid
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.Hyohyoid
The x.charAt test is not reliable and could introduce security flaws.Ragan
this a moo point, Performance just matter after Feature, after standardizing test cases. it's not useful if you are comparing a code that output different resultsVeranda
C
19

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

Castera answered 28/5, 2018 at 15:47 Comment(4)
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.Pouliot
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.Alginate
here is a benchmark, 30x faster on firefox, 2 nanoseconds vs 50 nsHorick
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 performanceCastera
F
16

I like to use this simple solution:

var myString = "test";
if(myString.constructor === String)
{
     //It's a string
}
Frontward answered 6/11, 2017 at 21:21 Comment(8)
How is that different from Cody's answer, 4 years later?Argos
@Sheljohn Cody's answer is great. My answer (complete text) is shorter and straight to the point. You asked... :)Frontward
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('')).Hyohyoid
@Hyohyoid No problem: (mystring || false) && mystring.constructor === String. I used false in case it's used in a function that must return a boolean.Pound
@Hyohyoid - do empty strings return a different answer for .constructor? That would be quite surprising.Testudo
No, just pointing out that "" and new String("") have different boolean values, which complicates getting the code right. I think code someone else suggested in these comments doesn't quite work, for that very reason.Hyohyoid
So we need something like myString === '' || (!!myString && myString.constructor === String)Hyohyoid
@Hyohyoid or just myString != null && myString.constructor === String. But yeah, null and undefined are complications that this answer doesn't handle as written, so -1.Inainability
H
16

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()
Hecker answered 10/8, 2019 at 15:34 Comment(8)
var x = new String(x); String(x)===x returns false. however ({}).toString.call(x).search(/String/)>0 always returns for stringy thingsAegrotat
function isClass(x,re){return ({}).toString.call(x).search(re)>0;}; isClass("hello",/String/) or isClass(3,/Number/) or isClass(null,/Null/)Aegrotat
This technique seems "non obvious" to me. Clever techniques that "work", but don't clearly express the intent, I find distasteful.Testudo
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 idempotenceHecker
@Aegrotat 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.Inainability
Number('1') === 1 returns true, though. Number.isInteger('1') is false.Gavette
name = 1; String(name) === name; return true, too.Gavette
@Faither Number("1") equals 1, Number converts the string "1" to 1. And Number.isInteger("1") is false because "1" is a string, not an integer. And name = 1; String(name) === name is true because global name is a window property and will always be a string, name = 1; typeof name will show string. I updated my answer with test examples.Hecker
J
14
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')

Janniejanos answered 16/11, 2019 at 15:49 Comment(1)
Attention! This will fail on empty strings, since those are falsey.Burris
R
11

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
Rese answered 6/9, 2015 at 5:6 Comment(1)
If someone wants to know the source, it's github.com/lodash/lodash/blob/master/isString.jsHaunted
N
9

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
Norikonorina answered 15/4, 2019 at 15:48 Comment(2)
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.Inainability
A ridiculous counterexample is Object.assign('abc', { [Symbol.toStringTag]: 'notAString' }) - it's a string, but try passing it to type!Ragan
C
9

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

Coarctate answered 20/7, 2021 at 8:37 Comment(3)
your solution is the best here, and also works for other objects, as in the solution offered by @Orwellophile.Veneering
Best approach. Less work than having | clausesFlourishing
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)Ragan
M
5

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;
}
Monogamy answered 21/1, 2023 at 12:9 Comment(0)
R
4

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.

Rosado answered 27/9, 2013 at 18:9 Comment(7)
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.Milch
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.Rosado
this does not work with the String type; var s = new String('abc'); > s === s + '' > falseDunstable
Doesn't work with new String cus that creates a type of object. w3schools.com/js/tryit.asp?filename=tryjs_string_object2Rosado
Good thought, but leaves out the edge case of object wrapped strings.Detradetract
I find this approach distasteful. Writing good code isn't about making it shorter. Its about saying what you mean.Testudo
I think this works, but it's pretty non-obvious why to someone who doesn't happen to know that doing myVar + '' will always produce a string regardless of the type of myVar. I agree with @ToolmakerSteve's take; this is needlessly clever to the point of being obfuscated, and I see no reason to use it over typeof myVar == "string".Inainability
D
4
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))
Deltadeltaic answered 22/12, 2015 at 4:59 Comment(2)
Why do you need to check for null or undefined if x.constructor === String would also return false for null or undefined?Mesothorax
@JulesManson: It would throw an error, not produce false.Knot
L
4

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
Lyonnaise answered 19/7, 2018 at 4:33 Comment(2)
-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?Inainability
@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.Lyonnaise
W
2

A simple solution would be:

var x = "hello"

if(x === x.toString()){
// it's a string 
}else{
// it isn't
}
Waffle answered 25/2, 2015 at 15:0 Comment(5)
this doesn't checks if it's a string. It makes into a string, lots of things have toString() functionDolt
@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.Eldreda
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 exceptionDunstable
The idea is still usable. x === String(x) is safe and works.Woodcut
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.Richmond
S
2

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
Savdeep answered 24/10, 2018 at 4:14 Comment(0)
M
2

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)

Moujik answered 1/10, 2019 at 12:33 Comment(0)
V
2

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"
Valona answered 1/2, 2020 at 5:56 Comment(0)
H
2

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;
Harbison answered 11/9, 2021 at 10:18 Comment(0)
L
1

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.

Lightfingered answered 3/9, 2014 at 21:18 Comment(0)
I
1

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

if(maybeAString.toUpperCase)
  weHaveAString(maybeAString)

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

Infidel answered 25/4, 2022 at 23:17 Comment(6)
@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.Lanalanae
@andreyrk Did you even try it before you commented? Paste this into your JS console: let x = 123; console.log(x.toUpperCase());Lupelupee
@Lupelupee Reread the answer and check if your code matches. Hint: toUpperCase is not the same as toUpperCase()Lanalanae
@andreyrk Ah, you're right. I misread it.Lupelupee
Awesome! `const isString = x => !!x.toUpperCaseSchacker
@andreyrk that could still actually throw in case maybeAString is null or undefined. Worse, it can completely fail if we get a custom class that has a method or any property called toUpperCase.Actinism
S
1

I needed to check if the variable is a string and wanted to do it "quick and dirty". Here is my approach. Since only string have replaceAll method this is enough eficient.

const isString =  (input) => typeof input?.replaceAll === 'function'
// Examples of usage
console.log(isString(true))
// For both of them it will be true
console.log(isString(new String('test')))
console.log(isString('test'))
Statolith answered 11/10, 2023 at 14:21 Comment(1)
It's simple and works. Thanks.Holotype
G
0

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);
Gimp answered 7/10, 2021 at 11:30 Comment(0)
S
0

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
Spalding answered 27/4, 2022 at 15:9 Comment(0)
R
0

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)}`); 
}
Ragan answered 18/5, 2023 at 22:4 Comment(0)
S
-2

Below are two simple functions that should be fairly fast and work correctly for all the different types of string instances/variables. One for a scalar value check and the other for a vector of values to check.

const isString = x => ![null, undefined].includes(x) && !!x.toUpperCase
['', 'a', new String, String('a'), String(1)].map(v => isString(v)) // [true, true, true, true, true]
[null, undefined, 0, 1, true, [], ['a'], {}, {a: 'a'}].map(v => isString(v)) // [false, false, false, false, false, false, false, false, false]
const isStrings = x => x.map(v => ![null, undefined].includes(v) && !!v.toUpperCase).reduce((a, b) => a && b, true)
isStrings([]) // true
isStrings(['', 'a', new String, String('a'), String(1)]) // true
isStrings(['', 'a', new String, String('a'), String(1), 1]) // false
isStrings([0, 1, true, [], ['a'], {}, {a: 'a'}]) // false
Schacker answered 13/3, 2023 at 10:28 Comment(0)
B
-3

I'm not sure if you mean knowing if it's a type string regardless of its contents, or whether it's contents is a number or string, regardless of its type.

So to know if its type is a string, that's already been answered.
But to know based on its contents if its a string or a number, I would use this:

function isNumber(item) {
    return (parseInt(item) + '') === item;
}

And for some examples:

isNumber(123);   //true
isNumber('123'); //true
isNumber('123a');//false
isNumber('');    //false
Bluecoat answered 15/5, 2017 at 5:22 Comment(1)
I think I was originally asking how to check the type, although I didn't know how to even form the question back then. (and I'd probably just do this with /^\d+$/.test('123') to avoid the intricacies of potential parsing issues)Milch

© 2022 - 2024 — McMap. All rights reserved.