How can I determine whether a variable is a string or something else in JavaScript?
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)}`);
}
- The Google JavaScript Style Guide says to never use primitive object wrappers.
- Douglas Crockford recommended that primitive object wrappers be deprecated.
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 typeof
be deprecated as well? –
Jarrett typeof
be deprecated. –
Jarrett string
that way? Seems like bad practice. –
Paralysis '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 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)}`);
}
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 postMessage
d 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 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/KAwht0ep –
Contribute 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 postmessage
d?" - 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 Object.prototype.toString
method and break code (which is highly unlikely to happen, but still...) 2. It is much slower to execute. –
Contribute myVar.constructor === String
, it works for both cases: myVar = "Foobar"
or myVar = new String("Foobar")
–
Curlicue 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 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 new
keyword. Using String(someValue)
is used for converting value (eg. number) to string, not for creating. –
Contribute 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 prototype –
Ganef let a:any = 'some string'; console.log(a instanceof String)
results in false
on my machine. –
Jowett !!('some string', String)
which return true
. –
Banshee ('some string', String)
will always return String
, which will always be truthy regardless of the type of the first argument. –
Keeley string
to have a method or property called trim
. –
Keeley 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 trim
method or property, as well as any arbitrary object. –
Keeley 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 'use strict'
inside the function, the primitive version will be faster (300x). –
Knot "somestring" instanceof String
resolves to false
–
Filar new String("somestring") instanceof String
resolves to true
–
Contribute typeof
"somestring" === string, the second test is for String objects ``new String("somestring" instanceof String
. –
Testudo "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 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 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 instanceof
should come first: if (!(myVar instanceof String) || typeof myVar !== 'string') { // It's something else }
–
Misbecome instaceof
for this purpose. JSON.stringify({}) instanceof String
guess what? it's false
. But typeof JSON.stringify({}) === 'string'
is true
–
Italic 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)}`);
}
- The Google JavaScript Style Guide says to never use primitive object wrappers.
- Douglas Crockford recommended that primitive object wrappers be deprecated.
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 typeof
be deprecated as well? –
Jarrett typeof
be deprecated. –
Jarrett string
that way? Seems like bad practice. –
Paralysis '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 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)}`);
}
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 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 myObject+"" === myObject
won't work in the case myObject = new String("string")
. you have to use myObject instanceof String
. See DRAX's answer –
Medovich 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 NaN
. Check. toString.call(1-"a") => "[object Number]"
. But actually 1-"a" ==> NaN
–
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 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 isAnything
to Object.prototype.toString.call(arg) == '[object ${Anything}]
so I am always covered, even if I need something rare like isArguments
–
Verst toString
for objects standardised? Or could a future standards-compliant browser implement Object.toString as rendering "[Dude its an Object!]"
? –
Fleecy toString.call()
, this cannot be exploited with something like x = { toString: () => '[object Number]' }
. –
Argos 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 typeof
. –
Biotin 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 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 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.
_.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 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)) {}
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 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/
Object.prototype.toString.call(obj) === '[object String]'
? –
Parotid (x === y)
has better readability than x === y
? –
Parotid var a = ((1) + (1))
? return
is a statement, not a function call. –
Carreno 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 :)
(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 if(thing.call) { 'its a function'; }
or if(thing.defineProperties) { 'its an object'; }
. Thanks for the input, axkibe! –
Kinase 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'));
typeof
). Point taken nonetheless. Thank you. –
Urochrome 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 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) andx===x+''
(H) are fast/fastest - solution
_.isString
(lodash lib) is medium/fast - solutions B and K are slowest
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
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
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 ==
instead of ===
- but not sure if this matters. –
Hyohyoid x.charAt
test is not reliable and could introduce security flaws. –
Ragan 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
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 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 –
Castera I like to use this simple solution:
var myString = "test";
if(myString.constructor === String)
{
//It's a string
}
undefined
and null
, and still getting the answer right for empty strings (both ''
and new String('')
). –
Hyohyoid (mystring || false) && mystring.constructor === String
. I used false in case it's used in a function that must return a boolean. –
Pound .constructor
? That would be quite surprising. –
Testudo ""
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 myString === '' || (!!myString && myString.constructor === String)
–
Hyohyoid myString != null && myString.constructor === String
. But yeah, null
and undefined
are complications that this answer doesn't handle as written, so -1. –
Inainability 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()
var x = new String(x); String(x)===x
returns false. however ({}).toString.call(x).search(/String/)>0
always returns for stringy things –
Aegrotat function isClass(x,re){return ({}).toString.call(x).search(re)>0;};
isClass("hello",/String/)
or isClass(3,/Number/)
or isClass(null,/Null/)
–
Aegrotat 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 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 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')
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
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
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 Object.assign('abc', { [Symbol.toStringTag]: 'notAString' })
- it's a string, but try passing it to type
! –
Ragan 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
|
clauses –
Flourishing 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 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;
}
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.
typeof
. –
Milch typeof
but still quite a bit faster than toString
. Either way, I guess I just like the syntax for coercing. –
Rosado var s = new String('abc'); > s === s + '' > false
–
Dunstable new String
cus that creates a type of object
. w3schools.com/js/tryit.asp?filename=tryjs_string_object2 –
Rosado 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 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))
false
. –
Knot 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
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 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 A simple solution would be:
var x = "hello"
if(x === x.toString()){
// it's a string
}else{
// it isn't
}
toString()
function –
Dolt .toString
on any values; try if the x to be checked is null or undefined, your code throw exception –
Dunstable 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 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
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
)
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"
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;
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 null
s and undefined
types, and it will take care of non-string types, such as 0
.
I have a technique that's stupid. But straightforward.
if(maybeAString.toUpperCase)
weHaveAString(maybeAString)
Yeah, it's far from perfect. But it is straightforward.
let x = 123; console.log(x.toUpperCase());
–
Lupelupee toUpperCase
is not the same as toUpperCase()
–
Lanalanae 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 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'))
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);
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
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)}`);
}
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
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
/^\d+$/.test('123')
to avoid the intricacies of potential parsing issues) –
Milch © 2022 - 2024 — McMap. All rights reserved.