How can I determine whether a variable is a string or something else in JavaScript?
This question is related to
javascript
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.
For all browsers (and both test cases)
typeof||instanceof
(A, I) and x===x+''
(H) are fast/fastest_.isString
(lodash lib) is medium/fastUpdate: 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.
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://stackoverflow.com/a/9436948/860099
function A(x) {
return (typeof x == 'string') || (x instanceof String)
}
// https://stackoverflow.com/a/17772086/860099
function B(x) {
return Object.prototype.toString.call(x) === "[object String]"
}
// https://stackoverflow.com/a/20958909/860099
function C(x) {
return _.isString(x);
}
// https://stackoverflow.com/a/20958909/860099
function D(x) {
return $.type(x) === "string";
}
// https://stackoverflow.com/a/16215800/860099
function E(x) {
return x?.constructor === String;
}
// https://stackoverflow.com/a/42493631/860099
function F(x){
return x?.charAt != null
}
// https://stackoverflow.com/a/57443488/860099
function G(x){
return String(x) === x
}
// https://stackoverflow.com/a/19057360/860099
function H(x){
return x === x + ''
}
// https://stackoverflow.com/a/4059166/860099
function I(x) {
return typeof x == 'string'
}
// https://stackoverflow.com/a/28722301/860099
function J(x){
return x === x?.toString()
}
// https://stackoverflow.com/a/58892465/860099
function K(x){
return x && typeof x.valueOf() === "string"
}
// https://stackoverflow.com/a/9436948/860099
function L(x) {
return x instanceof String
}
// ------------------
// PRESENTATION
// ------------------
console.log('Solutions results for different inputs \n\n');
console.log("'abc' Str '' ' ' '1' '0' 1 0 {} [] true false null undef");
let tests = [ 'abc', new String("abc"),'',' ','1','0',1,0,{},[],true,false,null,undefined];
[A,B,C,D,E,F,G,H,I,J,K,L].map(f=> {
console.log(
`${f.name} ` + tests.map(v=> (1*!!f(v)) ).join` `
)})
_x000D_
<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!
_x000D_
And here are example results for chrome
Since 580+ people have voted for an incorrect answer, and 800+ have voted for a working but shotgun-style answer, I thought it might be worth redoing my answer in a simpler form that everybody can understand.
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))_x000D_
const fn2 = async () => ({})_x000D_
_x000D_
console.log('fn1', Object.prototype.toString.call(fn1))_x000D_
console.log('fn2', Object.prototype.toString.call(fn2))
_x000D_
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
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
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
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
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:
duckyString()
to normalise inputs for code that expects real stringstext = '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
.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});
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"
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/
This is what works for me:
if (typeof myVar === 'string' || myVar instanceof String)
// it's a string
else
// it's something else
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 =>_x000D_
console.assert_x000D_
( String(x) === x_x000D_
, `not a string: ${x}`_x000D_
)_x000D_
_x000D_
test("some string")_x000D_
test(123) // assertion failed_x000D_
test(0) // assertion failed_x000D_
test(/some regex/) // assertion failed_x000D_
test([ 5, 6 ]) // assertion failed_x000D_
test({ a: 1 }) // assertion failed_x000D_
test(x => x + 1) // assertion failed
_x000D_
The same technique works for Number too -
Number(x) === x // true, if x is a number
// false in every other case
const test = x =>_x000D_
console.assert_x000D_
( Number(x) === x_x000D_
, `not a number: ${x}`_x000D_
)_x000D_
_x000D_
test("some string") // assertion failed_x000D_
test(123) _x000D_
test(0) _x000D_
test(/some regex/) // assertion failed_x000D_
test([ 5, 6 ]) // assertion failed_x000D_
test({ a: 1 }) // assertion failed_x000D_
test(x => x + 1) // assertion failed
_x000D_
And for RegExp -
RegExp(x) === x // true, if x is a regexp
// false in every other case
const test = x =>_x000D_
console.assert_x000D_
( RegExp(x) === x_x000D_
, `not a regexp: ${x}`_x000D_
)_x000D_
_x000D_
test("some string") // assertion failed_x000D_
test(123) // assertion failed_x000D_
test(0) // assertion failed_x000D_
test(/some regex/) _x000D_
test([ 5, 6 ]) // assertion failed_x000D_
test({ a: 1 }) // assertion failed_x000D_
test(x => x + 1) // assertion failed
_x000D_
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 =>_x000D_
console.assert_x000D_
( Object(x) === x_x000D_
, `not an object: ${x}`_x000D_
)_x000D_
_x000D_
test("some string") // assertion failed_x000D_
test(123) // assertion failed_x000D_
test(0) // assertion failed_x000D_
test(/some regex/) _x000D_
test([ 5, 6 ]) _x000D_
test({ a: 1 }) _x000D_
test(x => x + 1)
_x000D_
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 =>_x000D_
console.assert_x000D_
( Array.isArray(x)_x000D_
, `not an array: ${x}`_x000D_
)_x000D_
_x000D_
test("some string") // assertion failed_x000D_
test(123) // assertion failed_x000D_
test(0) // assertion failed_x000D_
test(/some regex/) // assertion failed_x000D_
test([ 5, 6 ]) _x000D_
test({ a: 1 }) // assertion failed_x000D_
test(x => x + 1) // assertion failed
_x000D_
This technique does not work for Functions however -
Function(x) === x // always false
I like to use this simple solution:
var myString = "test";
if(myString.constructor === String)
{
//It's a string
}
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))
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.
A simple solution would be:
var x = "hello"
if(x === x.toString()){
// it's a string
}else{
// it isn't
}
I think that @customcommander solution should suffice in 90% of your cases:
typeof str === 'string'
Should serve you right (simply since normally there's no reason to have new String('something')
in your code).
If you're interested in handling the String
object as well (for example you expect some var from a 3rd party) then using lodash as @ClearCloud8 suggested seems like a clear, simple and elegant solution.
I would however suggest to be cautious with libraries such as lodash due to their size. Instead of doing
import _ from 'lodash'
...
_.isString(myVar)
Which brings the whole huge lodash object, I'd suggest something like:
import { isString as _isString } from 'lodash'
...
_isString(myVar)
And with simple bundling you should be fine (I refer here to client code).
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)) {}
Edit: as @Jehy mentioned, this is deprecated since v4.
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.
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 :)
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')
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]';_x000D_
_x000D_
console.log(isString('foo'));_x000D_
_x000D_
Object.prototype.toString = () => 42;_x000D_
_x000D_
console.log(isString('foo'));
_x000D_
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 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.
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
)
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
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
Source: Stackoverflow.com