How to abstract null/undefined checking with Flow? - node.js

sessionStorage.getItem() is treated as Maybe/Optional type by Flow. So the following is necessary to make the result usable as a string type that is not of Optional type or of Maybe type:
const accessToken1 = sessionStorage.getItem('accessToken')
if (!accessToken1) throw new Error('Unwrapping not possible because the variable is null or undefined!')
'Hello ' + accessToken1 // no complaints by Flow
Now I want to abstract the null/undefined checking, but Flow does not stop complaining about the possible null and undefined types:
function unwrap<T>(value: T): T {
if (!value) throw new Error('Unwrapping not possible because the variable is null or undefined!')
return value // at this point Flow should understand it cannot be of type Optional or Maybe
}
'Hello ' + unwrap('World!') // works
'Hello ' + unwrap(null) // complains as expected with "null This type cannot be added to string"
'Hello ' + unwrap(undefined) // complains as expected with "null This type cannot be added to string"
const nullString = 'null'
'Hello ' + unwrap(nullString) // works
const accessToken2 = sessionStorage.getItem('accessToken')
'Hello ' + unwrap(accessToken2) // null/undefined This type cannot be added to string
const accessToken3 = (sessionStorage.getItem('accessToken'): string) // null/undefined This type cannot be added to string
'Hello ' + unwrap(accessToken3) // no complaints by Flow

Your return type is widening the refinement back to its original type. Try
function unwrap<T>(value: ?T): T { // Note the `?T`
if (!value) throw new Error('Unwrapping not possible because the variable is null or undefined!')
return value // at this point Flow should understand it cannot be of type Optional or Maybe
}
Some of your comments seem misguided. Here are the necessary corrections that I see:
'Hello ' + unwrap(null) // Not an error (I've opted for runtime errors with my `throw`)
'Hello ' + unwrap(undefined) // Not an error (I've opted for runtime errors with my `throw`)

Related

Typescripts type 'string | string[]' is not assignable to type 'string', what is the 'string | string[]' type? how to cast them to string?

When I do TypeScript:
let token = req.headers['x-access-token'] || req.headers['authorization'] as string;
I have fellow error:
Argument of type 'string | string[]' is not assignable to parameter of type 'string'
Any one know what is 'string | string[]' type? I mean if I want use logical 'or' of two string in typescript. How to do it?
And How to cast 'string | string[]' type to string type?
Try
let token = (req.headers['x-access-token'] || req.headers['authorization']) as string;
The compiler thinks req.headers['some string'] is an array of string, when you cast one side of the or operator you get a type of string or array of string. So do the or on both of them and then coerce the result to be a string.
I guess you are using node.js. In this case req.headers is of type IncomingHttpHeaders which has an index-signature of: [header: string]: string | string[] | undefined;
That means, that req.headers['whatever'] can be of type string or string[] (array of string) or undefined
the first part of your logical-or req.headers['x-access-token'] has type string | string[] | undefined
because of the cast expression the 2nd part req.headers['authorization'] as string is of type string
the type of token is string | string[], because
when the first part is defined, it can be string | string[]
when the first part is undefined, or will use the 2nd part which is of type string
Hint
instead of req.headers['authorization'] you can use req.headers.authorization which is of type string | undefined.
interface IncomingHttpHeaders {
..
'authorization'?: string;
..
[header: string]: string | string[] | undefined;
}
Details
Note: the answer of Adrian Brand is fine and you can use it as is. For the sake of completion I'll just show a detailed way how you could handle all cases and explain the types:
const tokenValue= req.headers['x-access-token'] || req.headers['authorization'];
tokenValue is of type string | string[] | undefined.
Note, that it can also be undefined when none of the headers exist.
We could handle this case:
if (!tokenValue) throw Error('missing header')
After this check typescript is smart enough to know that tokenValue is now of type string | string[]
if (Array.isArray(tokenValue)) {
// In this if branch the type of `tokenValue` is `string[]`
} else {
// In this if branch, the type of `tokenValue` is `string`
}
Clarifications
Any one know what is 'string | string[]' type? I mean if I want use logical 'or' of two string in typescript. How to do it?
string | string[] is a type union (TS Docs) which means the relative value can be either a string OR string[] (or Array<string> or an array of strings).
The logical or operator || between two variables actually produces the union of the two variable types if and only if the left operand contains a falsish type (undefined, null, number, string and boolean), otherwise produces the first variable type. The falsish type is actually configuration dependent (see note below real solution). Example:
type NonFalsishType = { prop: number };
let var1: number | undefined = 42;
let var2: string = 'var2'
let var3: NonFalsishType = { prop: 42 };
let test1: number | string = var1 || var2;
let test2: number | string = var2 || var1;
let test3: string | NonFalsishType = var2 || var3;
// strictNullCheck = true
// string type can be omitted because NonFalsishType
// is defenitely not falsy
let test4: NonFalsishType = var3 || var2;
// strictNullCheck = false
// string not omitted because null can be assigned to var3
let test4: NonFalsishType | string/* | null*/ = var3 || var2;
And How to cast 'string | string[]' type to string type?
The "casting" (the correct name is type assertion (TS Docs), it is a semantically different concept) can be done in different ways, the most common is achieved by using the as keyword, but there is also the angle brackets notation:
// as
let myHeader: string = req.headers['authorization'] as string
// angle brackets
let myHeader: string = <string>req.headers['authorization']
Note: Type assertions do nothing at all during runtime, it will neither be compiled at all in JS code:
// TS
let myHeader: string = req.headers['authorization'] as string
// JS
var myHeader = req.headers['authorization'];
Type assertions are just ment to instruct the TS type checker to force a type to be restricted to another, ONLY during the type checking of the compilation phase. It is like saying to the compiler "I don't care which (of the union) type the variable actually is, treat it as it would be of this specified type"
Possible Solution
Now the easisest solution is assert the string type for your variable:
// parenthesis emphasize where the assertion is applied
let token: string = (req.headers['x-access-token'] as string) ||
(req.headers['authorization'] as string);
let token: string = (
req.headers['x-access-token'] ||
req.headers['authorization']
) as string;
// no runtime differences, just saying to the TS type checker
// two different way to see the same variables
This solution lead to different problems: what if the client sends to the server multiple x-access-token/authorization headers?
You will end up with an array in the token variable, which means that your produced code could break (eg token.substr(10) will produce a runtime error since arrays do not have substr property, which strings have).
Even worse if the client does not send x-access-token/authorization header at all (undefined property will break the execution with any accessor).
Real solution
You need to think on what you need to achieve. The TypeScript type notations are not there just for decorate your code, but actually to produce a significant quality code through type and pattern checking. You should not ignore the fact that a variable can take multiple types, otherwise you will have bugs and security issues in a production environment.
If your real intent is to validate an access token you should be sure that the token is non-empty AND unique in order to identify an user:
// usually is a bad practice to authorize with multiple headers
// but it does not produce significant runtime error doing this
let token: string | string[] | undefined = req.headers['x-access-token'] || req.headers['authorization'];
if (typeof(token) === 'undefined') {
// no token passed in the header
// token variable is of type 'undefined' in this scope
// probably here, if the page require access, you should
// respond with a 401 unauth code
// you can skip this check by appending a check at
// the end of token variable initialization like this:
// let token: string | string[] = ... || '';
}
else if (Array.isArray(token)) {
// multiple tokens passed in the header
// token variable is of type 'string[]' in this scope
// for this special case see multiple tokens notes (at the end)
}
else if (!token) {
// the header contains the token but is actually an empty string
// token variable is of type 'string' in this scope
// same as undefined token, if the page require access, you should
// respond with a 401 unauth code
}
else {
// the header contains a non-empty string token
// token variable is of type 'string' also in this scope
// validate your token and respond by consequence (200 OK / 401 unath)
}
Note:
req.headers[key], as stated by #TmTron's answer, is of type string | string[] | undefined, but undefined is not mentioned in the union type in the error. This because it is possible to configure TypeScript (in the tsconfig.json or by passing the proper command line argument) to ignore falsy values during the type checking (like false, null and undefined). The option is strictNullCheck (TS Docs), and by default is set to false (meaning that TS will ignore the falsy values while type checking). If you put that option to true the error would become:
Argument of type 'string | string[] | undefined' is not assignable to parameter of type 'string'
forcing you to take in account also the undefined case (which in my experience usually prevents many and very many bugs)
Multiple Tokens Notes
The case of multiple tokens is more fuzzy, you should take an agreement
with your intents:
always reject multiple tokens - best, suggested and common practice (401 unath)
reject multiple tokens if they refer to different users - if there is the possibility, like ignore and drop elasped tokens but check they refer to the same user (validate token - 200 OK / 401 unath)
accept as valid only the first token: just use token = token[0] || '' and remove the else in the subsequent else if (becoming if (!token) ...) - still viable but not really a clean solution
Pratically there are some authentication techniques which make use of extended tokens (coma separated tokens), but are very scarce in the daily usage of security implementations.
Also note that teoretically a client should not send multiple headers with the same name, but actually a malicious user could simulate a call to your server with repeated headers in order to exploit some of the vulnerabilities of your application. And this is the reason why you should validate also the array case.
This is because it's possible to have multiple of the same header.
Here I've returned either the header or if it's an array the first instance of that header.
const getHeader = (name) => Array.isArray(req.headers[name]) ? req.headers[name][0] : req.headers[name];
let token = getHeader('x-access-token') ?? getHeader('authorization');

How to assign an index signature to a object's value?

Giving the following code:
export function objToStr(object: object): string {
let str = [];
for (let p in object) {
if (object.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(object[p]))
}
}
return str.join("&")
}
I get the error from object[p]:
Element implicitly has an 'any' type because type '{}' has no index signature. [7017]
I tried with
encodeURIComponent((<any>object[p]))
encodeURIComponent(object[p]: any)
But I still get the error. I find typing everything quite confusing, there is so much types out there.
If someone can tell me what Typescript wants from me there, it would help a lot, it's the last error message and then I'm done switching my code form JS to TS.
EDIT
I had to add "noImplicitAny": true to test the setting as I wasn't sure what is was doing and how the code would react to it.
Turning it to false I now get:
Argument of type 'string' is not assignable to parameter of type 'never'. [2345] for the part insite str.push
Error appears because you have compilerOptions.noImplicitAny = true at tsconfig.json. As error suggests, add index signature to object variable:
let object: { [index: string]: any } = {};
let str: string[] = [];
i.e. any will be specified explicit.

Can someone explan this error: expression statement is not assignment or call

I have these two blocks of code.
socket.on('chatMessage', function(message) {
message.type = 'message';
message.created = Date.now();
message.username : socket.request.user.username;
});
socket.on('disconnect', function() {
io.emit('chatMessage', {
type: 'status',
text: socket.request.user.username + ' left the conversation.',
created: Date.now(),
username: socket.request.user.username,
});
});
If i change : or = to other, Webstorm gives me error.Expression statement is not assignment or call. Could someone explain why this happens? Thanks in advance.
Your first block of code is defining a function block. Your second block of code is defining an object definition. There is different syntax allowed for each and you have to use the appropriate syntax to match the context.
Your first block of code is just executing a series of statements in a function block. The { at the end of this line:
socket.on('chatMessage', function(message) {
defines the beginning of a function block. Thus, Javascript is looking for statements that are legal in that context. Each line of code there needs to be a legal Javascript statement. And,
message.username : socket.request.user.username;
is not a legal statement in that context. If you were trying to assign a value to message.username, then the valid syntax for that would be:
message.username = socket.request.user.username;
In your second block, you are in the middle of an object definition. The { at the end of this line:
io.emit('chatMessage', {
starts an object definition. As such, the legal syntax for defining a property on that object looks like this:
username: socket.request.user.username,
So, there are two keys here:
Recognizing when a { signifies the start of a function block vs. the start of an object definition. Your first code block starts a function body, the second starts an object definition.
Knowing the difference between syntax allowed in a function block and syntax allowed in an object definition.
This is ok:
message.type = 'message';
This is ok:
message.created = Date.now();
This is not:
message.username : socket.request.user.username;
It should be:
message.username = socket.request.user.username;
Your question is not clear - do you get the warning when you change the : to = or before you change it. You didn't say which line causes the error, I assumed that it was the one with : instead of =.
Are you sure that you're not using TypeScript that could interpret the colons differently?

error: invalid input syntax for integer: when passing strings

I have the following function in node.js that makes a query to postgres based based on name_url. Sometimes it works and sometimes it just doesn't work.
Also I'm using lib pg-promise:
exports.getLawyerByUrlName = function (name_url, callback) {
console.log(typeof name_url) //NOTICE: output string
db.one({
text: "SELECT * FROM " + lawyersTable + " WHERE name_url LIKE $1::varchar",
values: name_url,
name: "get-lawyer-by-name_url"
})
.then(function (lawyer) {
callback(lawyer);
})
.catch(function (err) {
console.log("getLawyerByUrlName() " + err)
});
}
When it does not work it throws error:
getLawyerByUrlName() error: invalid input syntax for integer: "roberto-guzman-barquero"
This is a very weird bug I can't seem to catch why its happening. I'm checking before in console.log that I'm actually passing a string:
console.log(typeof name_url) //NOTICE: output string
My table field for name_url is:
CREATE TABLE lawyers(
...
name_url VARCHAR check(translate(name_url, 'abcdefghijklmnopqrstuvwxyz-', '') = '') NOT NULL UNIQUE,
It seems to be unlikely that that particular query could ever throw that error so I'll suggest three possibilities. The first is that the code that's causing the error is actually someplace else and that:
.catch(function (err) {
console.log("getLawyerByUrlName() " + err)
was cut and pasted into a different part of the code.
The 2nd possibility is that the "lawersTable" variable is getting populated with something unexpected.
The 3rd possibility is that my first two scenarios are wrong. ;-)

Expression.Call and "Ambiguous Match Found"

I'm trying to write an expression that will call ToString on a property and assign it's value to a local variable. However, calling ToString on a object instance w/ an overload of ToString, causes an exception of "Ambigous Match Found" to be thrown. Here's an example:
var result = Expression.Variable(typeof(string), "result");
var matchTypeParameter = Expression.Parameter(typeof(MatchType), "matchType");
var targetProperty = Expression.Property(leadParameter, target);
var exp = Expression.Block(
//Add the local current value variable
new[] { result },
//Get the target value
Expression.Assign(result, Expression.Call(targetProperty, typeof(string).GetMethod("ToString"), null))
);
How can I call ToString if the instance has overloads for it? Thanks!
Replace:
typeof(string).GetMethod("ToString")
With:
typeof(string).GetMethod("ToString", Type.EmptyTypes)
In other words, get the method named "ToString" that takes zero arguments (empty type array).

Resources