Why does String(null) work? - javascript

null and undefined don't have a toString or valueOf method. Afaik using String calls the toString method of its parameter (e.g. String({}) => [object Object]).
Why do String(null) or String(undefined work then? It doesn't implicitly do Object.prototype.toString.call(null). because that evaluates to [object Null].
[edit]: from the spec ECMA-262/5th edition (page 48). This doesn't add to clarification, I'd say:
/*
Table 13 — ToString Conversions
-------------------------------------------------------------------------
Argument Type | Result
-------------------------------------------------------------------------
Undefined | "undefined"
Null | "null"
Boolean | If the argument is true, then the result is "true".
... | ...
*/

After reviewing my previous answer, it seems a complete overhaul of my previous answer is necessary. I was way over complicating it, as the short answer is that these are standards-specified special cases.
The specification for String() (String used as a function):
15.5.1.1 String ( [ value ] )
Returns a String value (not a String object) computed by ToString(value). If value is not supplied, the empty
String "" is returned.
The ToString function (that exists internally, not in userland) is defined as follows (9.8):
"The abstract operation ToString converts its argument to a value of type String according to Table 13"
Argument Type | Result
Null | "null"
Undefined | "undefined"
This means that String(null) and String(undefined) go into this special table of types and just return the string values valued "null" and "undefined".
A user-land pseudo-implementation looks something like this:
function MyString(val) {
if (arguments.length === 0) {
return "";
} else if (typeof val === "undefined") {
return "undefined";
} else if (val === null) {
return "null";
} else if (typeof val === "boolean") {
return val ? "true" : "false";
} else if (typeof val === "number") {
// super complex rules
} else if (typeof val === "string") {
return val;
} else {
// return MyString(ToPrimitive(val, prefer string))
}
}
(Note that this example ignores the constructor case (new MyString()) and that it uses user-land concepts rather than engine-land.)
I got a bit carried away and found an example implementation (V8 to be specific):
string.js:
// Set the String function and constructor.
%SetCode($String, function(x) {
var value = %_ArgumentsLength() == 0 ? '' : TO_STRING_INLINE(x);
if (%_IsConstructCall()) {
%_SetValueOf(this, value);
} else {
return value;
}
});
macros.py:
macro TO_STRING_INLINE(arg) = (IS_STRING(%IS_VAR(arg)) ? arg : NonStringToString(arg));
runtime.js:
function NonStringToString(x) {
if (IS_NUMBER(x)) return %_NumberToString(x);
if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
if (IS_UNDEFINED(x)) return 'undefined';
return (IS_NULL(x)) ? 'null' : %ToString(%DefaultString(x));
}
The NonStringToString (which is essentially what is of interest), is luckily defined in psuedo-JS-land. As you can see, there is indeed a special case for null/true/false/undefined.

There is probably just some extra checks and handling for special cases like null and undefined.
MDN says:
It's possible to use String as a "safer" toString alternative, as although it still normally calls the underlying toString, it also works for null and undefined.

String(null) creates a string object and passes it a default value of null.

You might be interested in seeing the Annotated ES5 (which is much more readable than the ECMAScript 5 PDF) which states that: new String([ value ]) http://es5.github.com/#x15.5.2.1 calls [ToString] http://es5.github.com/#x9.8 (there is a table of the special convertion cases) to convert the value passed to it to a string.

Related

typescript type error with object property in a variable: obj[variable]

I'm new with Typescript and I have a problem with using a variable to choose the property of an object.
I have a function that filters an array on a property passed as a parameter. The function receives 3 parameters: an array of objects, the characters sought and the property that will be filtered.
here is the function.
type Data = {
[key: string]: string | number;
};
function filtreTexte(arr: Data[], searchCar: string, field: string): Data[] {
return arr.filter((el) => {
if (el[field] === "string") {
return el[field].toLowerCase().indexOf(searchCar.toLowerCase()) !== -1;
}
})
}
The problem on the .toLowerCase() which tells me that the method does not work on a number|string type even though I am testing if it is indeed a string.
If I change the el[field] to el.name, the error is no longer mentioned.
How should I do it ?
Thank you in advance for your help.
Your check for the type needs to look like this: if (typeof el[field] === "string") rather than if (el[field] === "string") - the latter case will only be true if the value of el[field] is exactly 'string'.
What #Dakeyras said is correct. You should add the typeof operator.
But the narrowing will still not work. Even though we checked if the type of el[field] is string, the compiler will inconveniently forget this fact when we try to use it next. Generally the compiler would not try to narrow the type in this scenario where el has an index signature and field is just some string. A lot of things could break if that would happen. For example, field could have changed between the check and later usage. For a further discussion on all the different limitations of TypeScript's Control Flow Analysis, see this.
To make this work, assign el[field] to an extra variable val, perform the narrowing on val and then use val later. Also don't forget to add another return statement to make the "Not all code paths return a value." error disappear.
function filtreTexte(arr: Data[], searchCar: string, field: string): Data[] {
return arr.filter((el) => {
const val = el[field]
if (typeof val === "string") {
return val.toLowerCase().indexOf(searchCar.toLowerCase()) !== -1;
}
return false
});
}
Playground
You need to wrap the el[field] into an variable declaration, this offers more specific info about the variable type since was declard before, el[field] is dynamic value wich can be string or number, so this will thrown an error.
const element = el[field];
if (typeof element === 'string') {
return element.toLowerCase().indexOf(searchCar.toLowerCase()) !== -1;
}
Also, it works with el.name because is a specific property, the same way as the above example.
Hope it helps.

Type not inferred when accessed as object key

Given the following example:
type Dictionary = {
[key: string] : string | undefined
}
function greet(name: string) {
return 'Hello ' + name + '!';
}
function callGreetError(key: string, d: Dictionary) {
if (typeof d[key] !== 'undefined') {
return greet(d[key]) // TS Error: Argument of type 'string | undefined' is not assignable to parameter of type 'string'.Type 'undefined' is not assignable to type 'string'.(2345)
}
return key + ' is not in dictionary';
}
function callGreetNoError(key: string, d: Dictionary) {
const storedVal = d[key];
if (typeof storedVal !== 'undefined') {
return greet(storedVal) // Error goes away when value is stored in an external var.
}
return key + ' is not in dictionary';
}
I'm trying to understand why on callGreetError the type of d[key] inside the if block isn't inferred to be a string even though I'm explicitly telling TS it isn't undefined.
And why storing the value of d[key] on an external var storedVal on callGreetNoError fixes this error.
Basically TS won't narrow the property access type of computed property names like key.
const d: Dictionary = {...};
const key = "foo";
const fooProp = d[key];
// using .length as string type check
d["foo"] !== undefined && d["foo"].length; // ✔
fooProp !== undefined && fooProp.length; // ✔
d[key] !== undefined && d[key].length; // error, possibly undefined
It is not, because TS does some mutability check and warns that d[key] value could have changed between the check and its usage. For example following code is perfectly fine for the compiler, but may throw at run-time:
const testDict: Dictionary = {
get foo() { return Math.random() > 0.5 ? "hey" : undefined }
};
function callGreetError(d: Dictionary) {
// compiles fine, but throws error at run-time from time to time
if (d["foo"] !== undefined) d["foo"].length
}
callGreetError(testDict)
To allow proper variable narrowing with control flow, TS must clearly know, what property you mean: by property access with dot notation d.foo or with bracket notation and a literal like d["foo"].
The "trick" with const storedVal = d[key] works, because TS infers the variable type of storedVal to be string | undefined. As control flow analysis in general is based on variables, the compiler now has an easier time narrowing down storedVal with a check for undefined.
Playground
Adding an ! to d[key] in the return statement tells TypeScript that d[key] will not be undefined in this case, although normally it can be. This is known as a non-null assertion operator.
function callGreetError(key: string, d: Dictionary) {
if (typeof d[key] !== 'undefined') {
return greet(d[key]!)
}
return key + ' is not in dictionary';
}
A new ! post-fix expression operator may be used to assert that its
operand is non-null and non-undefined in contexts where the type
checker is unable to conclude that fact. Specifically, the operation
x! produces a value of the type of x with null and undefined excluded.
Similar to type assertions of the forms x and x as T, the !
non-null assertion operator is simply removed in the emitted
JavaScript code.
Just create a temporary variable so you are not accessing the value separately every time. For all typescript knows the value could have changed between the check and its use:
function callGreetError(key: string, d: Dictionary) {
const temp = d[key];
if (temp !== undefined) {
return greet(temp)
}
return key + ' is not in dictionary';
}
In this case both checks typeof temp !== "undefined" and temp !== undefined would work
The simplest example showing the problem can be manually assigning a get operator to the dictionary key. In the code sample below you can see that the value of key would be indeed always a string, satisfying type requirements, however it changes with every access
const testDict: Dictionary = {};
Object.defineProperty(testDict, "key", {
get() { return Math.random().toString() }
});
console.log(testDict.key);
console.log(testDict.key);
Thus checking the type with first access and using it with the second is unrelated
Try doing it like this:
function callGreetError(key: string, d: Dictionary) {
return d?.[key]
? greet(d[key])
: key + ' is not in dictionary';
}
This way you're making sure that key is not undefined, and not only d[key]. If the syntax confuses you, you can read more about optional chaining here.

Why is (null==undefined) true in JavaScript? [duplicate]

How do I check a variable if it's null or undefined and what is the difference between the null and undefined?
What is the difference between == and === (it's hard to search Google for "===" )?
How do I check a variable if it's null or undefined...
Is the variable null:
if (a === null)
// or
if (a == null) // but see note below
...but note the latter will also be true if a is undefined.
Is it undefined:
if (typeof a === "undefined")
// or
if (a === undefined)
// or
if (a == undefined) // but see note below
...but again, note that the last one is vague; it will also be true if a is null.
Now, despite the above, the usual way to check for those is to use the fact that they're falsey:
if (!a) {
// `a` is falsey, which includes `undefined` and `null`
// (and `""`, and `0`, and `NaN`, and [of course] `false`)
}
This is defined by ToBoolean in the spec.
...and what is the difference between the null and undefined?
They're both values usually used to indicate the absence of something. undefined is the more generic one, used as the default value of variables until they're assigned some other value, as the value of function arguments that weren't provided when the function was called, and as the value you get when you ask an object for a property it doesn't have. But it can also be explicitly used in all of those situations. (There's a difference between an object not having a property, and having the property with the value undefined; there's a difference between calling a function with the value undefined for an argument, and leaving that argument off entirely.)
null is slightly more specific than undefined: It's a blank object reference. JavaScript is loosely typed, of course, but not all of the things JavaScript interacts with are loosely typed. If an API like the DOM in browsers needs an object reference that's blank, we use null, not undefined. And similarly, the DOM's getElementById operation returns an object reference — either a valid one (if it found the DOM element), or null (if it didn't).
Interestingly (or not), they're their own types. Which is to say, null is the only value in the Null type, and undefined is the only value in the Undefined type.
What is the difference between "==" and "==="
The only difference between them is that == will do type coercion to try to get the values to match, and === won't. So for instance "1" == 1 is true, because "1" coerces to 1. But "1" === 1 is false, because the types don't match. ("1" !== 1 is true.) The first (real) step of === is "Are the types of the operands the same?" and if the answer is "no", the result is false. If the types are the same, it does exactly what == does.
Type coercion uses quite complex rules and can have surprising results (for instance, "" == 0 is true).
More in the spec:
Abstract Equality Comparison (==, also called "loose" equality)
Strict Equality Comparison (===)
The difference is subtle.
In JavaScript an undefined variable is a variable that as never been declared, or never assigned a value. Let's say you declare var a; for instance, then a will be undefined, because it was never assigned any value.
But if you then assign a = null; then a will now be null. In JavaScript null is an object (try typeof null in a JavaScript console if you don't believe me), which means that null is a value (in fact even undefined is a value).
Example:
var a;
typeof a; # => "undefined"
a = null;
typeof null; # => "object"
This can prove useful in function arguments. You may want to have a default value, but consider null to be acceptable. In which case you may do:
function doSomething(first, second, optional) {
if (typeof optional === "undefined") {
optional = "three";
}
// do something
}
If you omit the optional parameter doSomething(1, 2) thenoptional will be the "three" string but if you pass doSomething(1, 2, null) then optional will be null.
As for the equal == and strictly equal === comparators, the first one is weakly type, while strictly equal also checks for the type of values. That means that 0 == "0" will return true; while 0 === "0" will return false, because a number is not a string.
You may use those operators to check between undefined an null. For example:
null === null # => true
undefined === undefined # => true
undefined === null # => false
undefined == null # => true
The last case is interesting, because it allows you to check if a variable is either undefined or null and nothing else:
function test(val) {
return val == null;
}
test(null); # => true
test(undefined); # => true
The spec is the place to go for full answers to these questions. Here's a summary:
For a variable x, you can:
check whether it's null by direct comparison using ===. Example: x === null
check whether it's undefined by either of two basic methods: direct comparison with undefined or typeof. For various reasons, I prefer typeof x === "undefined".
check whether it's one of null and undefined by using == and relying on the slightly arcane type coercion rules that mean x == null does exactly what you want.
The basic difference between == and === is that if the operands are of different types, === will always return false while == will convert one or both operands into the same type using rules that lead to some slightly unintuitive behaviour. If the operands are of the same type (e.g. both are strings, such as in the typeof comparison above), == and === will behave exactly the same.
More reading:
Angus Croll's Truth, Equality and JavaScript
Andrea Giammarchi's JavaScript Coercion Demystified
comp.lang.javascript FAQs: JavaScript Type-Conversion
How do I check a variable if it's null or undefined
just check if a variable has a valid value like this :
if(variable)
it will return true if variable does't contain :
null
undefined
0
false
"" (an empty string)
NaN
undefined
It means the variable is not yet intialized .
Example :
var x;
if(x){ //you can check like this
//code.
}
equals(==)
It only check value is equals not datatype .
Example :
var x = true;
var y = new Boolean(true);
x == y ; //returns true
Because it checks only value .
Strict Equals(===)
Checks the value and datatype should be same .
Example :
var x = true;
var y = new Boolean(true);
x===y; //returns false.
Because it checks the datatype x is a primitive type and y is a boolean object .
Ad 1. null is not an identifier for a property of the global object, like undefined can be
let x; // undefined
let y=null; // null
let z=3; // has value
// 'w' // is undeclared
if(!x) console.log('x is null or undefined');
if(!y) console.log('y is null or undefined');
if(!z) console.log('z is null or undefined');
try { if(w) 0 } catch(e) { console.log('w is undeclared') }
// typeof not throw exception for undelared variabels
if(typeof w === 'undefined') console.log('w is undefined');
Ad 2. The === check values and types. The == dont require same types and made implicit conversion before comparison (using .valueOf() and .toString()). Here you have all (src):
if
== (its negation !=)
=== (its negation !==)
If your (logical) check is for a negation (!) and you want to capture both JS null and undefined (as different Browsers will give you different results) you would use the less restrictive comparison:
e.g.:
var ItemID = Item.get_id();
if (ItemID != null)
{
//do stuff
}
This will capture both null and undefined
Try With Different Logic. You can use bellow code for check all four(4) condition for validation like not null, not blank, not undefined and not zero only use this code (!(!(variable))) in javascript and jquery.
function myFunction() {
var data; //The Values can be like as null, blank, undefined, zero you can test
if(!(!(data)))
{
//If data has valid value
alert("data "+data);
}
else
{
//If data has null, blank, undefined, zero etc.
alert("data is "+data);
}
}

Typescript, JSON.parse error: "Type 'null' is not assignable to type 'string'."

The error was happening here:
let moonPortfolio;
...
moonPortfolio = JSON.parse(localStorage.getItem('moonPortfolio'));
I found this answer which makes sense, however I'm still getting that error after this refactor:
As the error says, localStorage.getItem() can return either a string or null. JSON.parse() requires a string, so you should test the result of localStorage.getItem() before you try to use it.
if (portfolio.length === 0) {
const storedPortfolio = localStorage.getItem('moonPortfolio');
if (typeof storedPortfolio === 'string') {
moonPortfolio = JSON.parse(localStorage.getItem('moonPortfolio'));
}
else {
moonPortfolio = [];
}
if (moonPortfolio) {
const savedPortfolio = Object.values(moonPortfolio);
this.props.fetchAllAssets();
// this.props.addCoins(savedPortfolio);
}
}
I first set the results of localStorage moonPortfolio to a var, then check if the var is typeof string. Yet still getting the typescript error?
Any thoughts or direction here?
Simple fix:
JSON.parse(localStorage.getItem('moonPortfolio') || '{}');
Seems like TS does know about the inner workings of localStorage/sessionStorage actually. It returns null if you try to fetch a key that isn't set. null when treated as boolean is falsy so by adding OR the empty stringified json object will be used instead meaning that JSON.parse(x) will always be given a string meaning it's then type safe.
The compiler doesn't know too much about the inner workings of localStorage.getItem and doesn't make the assumption that the return value will be the same from one call of getItem to the next. So it just tells you that it can't be certain that on the second call to getItem the result isn't null.
Try simply passing in the variable you've already created instead of reading from localStorage again:
if (typeof storedPortfolio === 'string') {
moonPortfolio = JSON.parse(storedPortfolio);
}
TypeScript doesn't know that multiple invocations of localStorage.getItem with the same string literal will always return the same value (in fact, this isn't even true).
The second call to localStorage.getItem('moonPortfolio') may well return null - you should call JSON.parse(storedPortfolio) instead of calling getItem again.
The main thing to know is that localStorage.getItem() returns string | null. Knowing that we can re-write the code with a simpler pattern:
const portfolio = []; //Just to make the code samples valid
if (portfolio.length === 0) {
let moonPortfolio = []; //Default value
const storedText = localStorage.getItem('moonPortfolio');
if (storedText !== null) { //We know it's a string then!
moonPortfolio = JSON.parse(storedText);
}
//The if statement here is not needed as JSON.parse can only return
//object | array | null or throw. null is the only falsy value and that
//can only happen if storedText is null but we've already disallowed
//that.
//if (moonPortfolio) {
const savedPortfolio = Object.values(moonPortfolio);
//Do whatever else is needed...
//}
}
also in react:
JSON.parse(localStorage.getItem("data") ?? '{}')
or
JSON.parse(localStorage.getItem("data") || '{}')

javascript - what kind of thing in a var

I've got either a literal object or one of it's components in var Z. Ie. one of the following
var Q = {"name" : 123};
Z = Q;
Z = Q["name"];
how can I determine which it is?
If this case, you can use typeof[mozilla docs] to check if the value is a number or not.
if (typeof z === "number") {
alert("I'm the property!");
} else {
alert("I'm the object!");
}
typeof x is useful if x is a primitive "number", "boolean", "string", "undefined" or "function", but you need more complicated checks for other types.
It's also possible for this check to behave unexpectedly if someone is using a object wrapper type instead of a primitive type. patrick dw's excellent answer provides a solution that can handle this.
A safer and more versatile approach that typeof is to find out its internal [[Class]] property by calling Object.prototype.toString with Z set as the calling context, like this:
var type = Object.prototype.toString.call( Z ); // [object ???]
The result will be in the form of [object Class], as in:
[object Object]
[object Array]
[object Number]
[object Boolean]
[object String]
You could easily make it into a function, something like this:
function getType( x ) {
return x === void 0 ? 'undefined' :
x === null ? 'null' :
Object.prototype.toString.call( x ).slice( 8, -1 ).toLowerCase();
}
This will return a lowercase string result for the type you want:
"string"
"number"
"array"
// ...etc
I made explicit tests for null and undefined because I think there may be browser incompatibilities with those, but I'm not sure. Anyway, they're easy enough to test explicitly.
So then you'd use it like:
if( getType( Z ) === "string" ) {
// do something
} else {
// do something else
}
The reason this is safer is that if a string for example happens to be given as its object wrapper, typeof will return "object" instead of "string".
typeof new String("test"); // "object"
Also, it covers the case of Array, which will always return "object" with typeof.
use typeof
Z = Q;
typeof Z == "object"; //true
Z = Q["name"];
typeof Q == "number"; //true
Yes, as the previous posts say, 'typeof' is the method you want.
It's worth knowing that typeof returns a string, even if the type is undefined or boolean for example so remember to use that in your checks.

Categories

Resources