Asked  7 Months ago    Answers:  5   Viewed   34 times

I saw this small piece of code that is evading my understanding:

<?php

$a = '0e462097431906509019562988736854';
$b = '0e830400451993494058024219903391';

var_dump($a == $b);

Which will output:

bool(true)

I understand that when using ==, PHP will attempt fuzzy comparison, silently converting between types in order to perform the comparison. What I'm not understanding is why PHP seems to think these two strings are the same. I would have thought since $a and $b are strings, that no type conversion would need to take place.

What am I not understanding?

 Answers

58

I think this article explains it pretty well:

Type-coercing comparison operators will convert numeric strings to numbers

Just to quote the main issue here:

According to php language.operators.comparison, the type-coercing comparison operators will coerce both operands to floats if they both look like numbers, even if they are both already strings:

where both strings are using exponential notation, hence are treated as numeric strings, making loose comparison (==), coerce these strings to floats before actually "loosely" comparing them.

As a best practice and to prevent unexpected behaviour, always try to use identity equality (===), especially when dealing with strings.

Wednesday, March 31, 2021
 
insomiac
answered 7 Months ago
20

And here's another hacky solution using the try {..} catch mechanism:

try {
    new ReflectionClass('ReflectionClass' . ((int)$SS . "" !== $SS));
    echo $SS;
} catch (Exception $e) {
    echo "Bad StringBad String";
}

It works like this: if the string $SS doesn't actually resolve to an integer, the code will try to lookup the ReflectionClass1 class, which, in the end, will throw a ReflectionException.

The parsing works by simply casting $SS to int and then back to string and comparing it to the initial value.

Saturday, May 29, 2021
 
daiscog
answered 5 Months ago
34

Unfortunately these things are not currently very well documented, but even if you were able to get it working, let's go over your configuration so that you understand what each part is doing and how it relates to how typescript processes and loads typings.

First let's go over the error you are receiving:

error TS2688: Cannot find type definition file for 'lodash'.

This error is actually not coming from your imports or references or your attempt to use lodash anywhere in your ts files. Rather it is coming from a misunderstanding of how to use the typeRoots and types properties, so let's go into a little more detail on those.

The thing about typeRoots:[] and types:[] properties is that they are NOT general-purpose ways to load arbitrary declaration (*.d.ts) files.

These two properties are directly related to the new TS 2.0 feature which allows packaging and loading typing declarations from NPM packages.

This is very important to understand, that these work only with folders in NPM format (i.e. a folder containing a package.json or index.d.ts).

The default for typeRoots is:

{
   "typeRoots" : ["node_modules/@types"]
}

By default this means that typescript will go into the node_modules/@types folder and try to load every sub-folder it finds there as a npm package.

It is important to understand that this will fail if a folder does not have an npm package-like structure.

This is what is happening in your case, and the source of your initial error.

You have switched typeRoot to be:

{
    "typeRoots" : ["./typings"]
}

This means that typescript will now scan the ./typings folder for subfolders and try to load each subfolder it finds as an npm module.

So let's pretend you just had typeRoots setup to point to ./typings but did not yet have any types:[] property setup. You would likely see these errors:

error TS2688: Cannot find type definition file for 'custom'.
error TS2688: Cannot find type definition file for 'global'.

This is because tsc is scanning your ./typings folder and finding the sub-folders custom and global. It then is trying to interpret these as npm package-type typing, but there is no index.d.ts or package.json in these folders and so you get the error.

Now let's talk a bit about the types: ['lodash'] property you are setting. What does this do? By default, typescript will load all sub-folders it finds within your typeRoots. If you specify a types: property it will only load those specific sub-folders.

In your case you are telling it to load the ./typings/lodash folder but it doesn't exist. This is why you get:

error TS2688: Cannot find type definition file for 'lodash'

So let's summarize what we've learned. Typescript 2.0 introduced typeRoots and types for loading declaration files packaged in npm packages. If you have custom typings or single loose d.ts files that are not contained withing a folder following npm package conventions, then these two new properties are not what you want to use. Typescript 2.0 does not really change how these would be consumed. You just have to include these files in your compilation context in one of the many standard ways:

  1. Directly including it in a .ts file: ///<reference path="../typings/custom/lodash.d.ts" />

  2. Including ./typings/custom/lodash.d.ts in your files: [] property.

  3. Including ./typings/index.d.ts in your files: [] property (which then recursively includes the other typings.

  4. Adding ./typings/** to your includes:

Hopefully, based on this discussion you'll be able to tell why the changes you mad to your tsconfig.json made things work again.

EDIT:

One thing that I forgot to mention is that typeRoots and types property are really only useful for the automatic loading of global declarations.

For example if you

npm install @types/jquery

And you are using the default tsconfig, then that jquery types package will be loaded automatically and $ will be available throughout all your scripts wihtout having to do any further ///<reference/> or import

The typeRoots:[] property is meant to add additional locations from where type packages will be loaded frrom automatically.

The types:[] property's primary use-case is to disable the automatic loading behavior (by setting it to an empty array), and then only listing specific types you want to include globally.

The other way to load type packages from the various typeRoots is to use the new ///<reference types="jquery" /> directive. Notice the types instead of path. Again, this is only useful for global declaration files, typically ones that don't do import/export.

Now, here's one of the things that causes confusion with typeRoots. Remember, I said that typeRoots is about the global inclusion of modules. But @types/folder is also involved in standard module-resolution (regardless of your typeRoots setting).

Specifically, explicitly importing modules always bypasses all includes, excludes, files, typeRoots and types options. So when you do:

import {MyType} from 'my-module';

All the above mentioned properties are completely ignored. The relevant properties during module resolution are baseUrl, paths, and moduleResolution.

Basically, when using node module resolution, it will start searching for a file name my-module.ts, my-module.tsx, my-module.d.ts starting at the folder pointed to by your baseUrl configuration.

If it doesn't find the file, then it will look for a folder named my-module and then search for a package.json with a typings property, if there is package.json or no typings property inside telling it which file to load it will then search for index.ts/tsx/d.ts within that folder.

If that's still not successful it will search for these same things in the node_modules folder starting at your baseUrl/node_modules.

In addition, if it doesn't find these, it will search baseUrl/node_modules/@types for all the same things.

If it still didn't find anything it will start going to the parent directory and search node_modules and node_modules/@types there. It will keep going up the directories until it reaches the root of your file system (even getting node-modules outside your project).

One thing I want to emphasize is that module resolution completely ignores any typeRoots you set. So if you configured typeRoots: ["./my-types"], this will not get searched during explicit module resolution. It only serves as a folder where you can put global definition files you want to make available to the whole application without further need to import or reference.

Lastly, you can override the module behavior with path mappings (i.e. the paths property). So for example, I mentioned that any custom typeRoots is not consulted when trying to resolve a module. But if you liked you can make this behavior happen as so:

"paths" :{
     "*": ["my-custom-types/*", "*"]
 }

What this does is for all imports that match the left-hand side, try modifying the import as in the right side before trying to include it (the * on the right hand side represents your initial import string. For example if you import:

import {MyType} from 'my-types';

It would first try the import as if you had written:

import {MyType} from 'my-custom-types/my-types'

And then if it didn't find it would try again wihtout the prefix (second item in the array is just * which means the initial import.

So this way you can add additional folders to search for custom declaration files or even custom .ts modules that you want to be able to import.

You can also create custom mappings for specific modules:

"paths" :{
   "*": ["my-types", "some/custom/folder/location/my-awesome-types-file"]
 }

This would let you do

import {MyType} from 'my-types';

But then read those types from some/custom/folder/location/my-awesome-types-file.d.ts

Thursday, June 3, 2021
 
ALH
answered 5 Months ago
ALH
91

For your example number 1, the alert is shown because you're using var inside the function and the var declaration is hoisted to the top of the function, so it is equivalent to:


var foo = 1; 
function bar() {
    var foo;
    if (!foo) {
        alert('inside if');
        foo = 10; 
    } 

} 
bar();

One might conclude that these sorts of issues offer compelling reason to declare all variables explicitly at the top of the function.

Friday, July 30, 2021
 
Whakkee
answered 3 Months ago
66

I'll start with map. The map function applies an operation to every element in a list. If I had

add3 :: Int -> Int
add3 x = x + 3

Then I could apply this to a whole list of Ints using map:

> map add3 [1, 2, 3, 4]
[4, 5, 6, 7]

So if you look at the type signature

map :: (a -> b) -> [a] -> [b]

You'll see that the first argument is (a -> b), which is just a function that takes an a and returns a b. The second argument is [a], which is a list of values of type a, and the return type [b], a list of values of type b. So in plain english, the map function applies a function to each element in a list of values, then returns the those values as a list.

This is what makes map a higher order function, it takes a function as an argument and does stuff with it. Another way to look at map is to add some parentheses to the type signature to make it

map :: (a -> b) -> ([a] -> [b])

So you can also think of it as a function that transforms a function from a to b into a function from [a] to [b].


The function ($) has the type

($) :: (a -> b) -> a -> b

And is used like

> add3 $ 1 + 1
5

All it does is take what's to the right, in this case 1 + 1, and passes it to the function on the left, here add3. Why is this important? It has a handy fixity, or operator precedence, that makes it equivalent to

> add3 (1 + 1)

So whatever to the right gets essentially wrapped in parentheses before being passed to the left. This just makes it useful for chaining several functions together:

> add3 $ add3 $ add3 $ add3 $ 1 + 1

is nicer than

> add3 (add3 (add3 (add3 (1 + 1))))

because you don't have to close parentheses.

Monday, August 2, 2021
 
whizzzkey
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :