Issue getting errorCallback from IPromise - javascript

I'm working with an existing TypeScript method and I'm struggling to get the errorCallback value from the promise. The Interface looks like the following from the Type Definition file for Angular:
interface IPromise<T> {
then<TResult>(successCallback: (promiseValue: T) => IHttpPromise<TResult>, errorCallback?: (reason: any) => any, notifyCallback?: (state: any) => any): IPromise<TResult>;
then<TResult>(successCallback: (promiseValue: T) => IPromise<TResult>, errorCallback?: (reason: any) => any, notifyCallback?: (state: any) => any): IPromise<TResult>;
then<TResult>(successCallback: (promiseValue: T) => TResult, errorCallback?: (reason: any) => TResult, notifyCallback?: (state: any) => any): IPromise<TResult>;
The TypeScript method I'm working with calls a service and the promise uses the return (this works):
public loadSavedLogin(): ng.IPromise<MyApp.Models.User> {
return this._myAppService.getUser(this.savedUserId).then((result: MyApp.Models.User) => {
if (result) {
this.userId = result.UserID;
this.userName = result.UserName;
}
return result;
});
}
The problem is I have no idea how to get the errorCallback value. If I place a comma after .then((result: MyApp.Models.User), I see Intellisense showing me the errorCallback parameter, but I just can't get any of the syntax working. In raw JS, I'd have a comma at the end with another function accepting the error value, but I'm not sure with this interface how to get the error returned.
How do I modify the function to get the error value if the service call returns one using IPromise?

Here is a simplified example to help you out.
class Test {
public _test: ng.IPromise<string>;
// This method has a return type of ng.IPromise<string>
// You must return a value of this type.
public example(): ng.IPromise<string> {
return this._test.then(
// Success
// Must return a string to be compatible with
// the ng.IPromise<string> return type
(val) => {
alert('Success');
return val;
},
// Error
// Should also return a string to be
// compatible with the return type
(reason) => {
alert('Error: ' + reason);
return '';
});
}
}
Because the example method return type is ng.IPromise<string>, the success function and the error function in the then method must return a string in order for the types to all match up.
In your case, they should return an instance of an MyApp.Models.User.
I suspect in your error function you weren't returning a value - but this makes the best common type between the success and error function void.
Further example... using just an array to show best common types when using functions:
var example = [
(input: string) => { return 'String'; },
(input: string) => { console.log(input); }
];
The best common type used in this example is (input: string) => void. Seems strange - but it actually makes sense. If you call the functions in this array, don't expect to get a return value.
So just make sure your success and error functions have the same return type and all the types will match up for you.
public loadSavedLogin(): ng.IPromise<MyApp.Models.User> {
return this._myAppService.getUser(this.savedUserId).then(
(result: MyApp.Models.User) => {
if (result) {
this.userId = result.UserID;
this.userName = result.UserName;
}
return result;
},
(reason: string) => {
return <MyApp.Models.User> null;
}
);
}

Related

How to pipe and chain an operation that uses multiple `Eithers` and `Promises` from `fp-ts`

i'm new on fp-ts, i'm trying to create a functional-like method that:
Parse a bearer token
Check if the user is valid using the parsed token
import { Request } from 'express';
import { either } from 'fp-ts';
import { pipe } from 'fp-ts/lib/function';
// * Parse the token
declare const parseRawToken: (rawToken: string | undefined) => either.Either<Error, string>;
// * Interface of the validate method
type MakeIsRequestAuthenticated = (
validateUserWithToken: (data: string) => Promise<either.Either<Error, void>>,
) => (request: Request) => Promise<either.Either<Error, void>>;
I want to chain these validations inside a pipe, so I tried to implement the validation by:
export const makeIsRequestAuthenticated: MakeIsRequestAuthenticated = validateUserWithToken => {
// * Validate the request
return async request => {
return pipe(
parseRawToken(request.header('Authorization')),
either.chain(validateUserWithToken)
);
};
};
but it gives my the following error:
Argument of type '(data: string) => Promise<Either<Error, void>>' is not assignable to parameter of type '(a: string) => Either<Error, void>'.
Type 'Promise<Either<Error, void>>' is not assignable to type 'Either<Error, void>'.ts(2345)
i had try replace the Promise by a TaskEither and some other solutions but none of them worked
I want to use the chain or may other some method to be able to execute all these operations inside the pipe
Hope this can help:
I've modified a litle the interface of the validate method to return the request, allowing another handler to take it and do whatever after validation
I've modified the interface of validateUserWithToken to return TaskEither<Error, void> instead of promise.
import { Request } from 'express';
import { pipe } from 'fp-ts/lib/function';
import * as TE from "fp-ts/TaskEither";
import * as E from "fp-ts/Either";
import { assert } from 'console';
// * Parse the token (dummy implementation)
const parseRawToken = (rawToken: string | undefined) =>
rawToken ? E.right(rawToken) : E.left(new Error("Invalid token"));
// * Interface of the validate method (changed return type to TastEither<Error, Request>)
type MakeIsRequestAuthenticated = (
validateUserWithToken: (data: string) => TE.TaskEither<Error, void>,
) => (request: Request) => TE.TaskEither<Error, Request>;
//Actual implementation
export const makeIsRequestAuthenticated: MakeIsRequestAuthenticated =
validateUser => (request: Request) =>
pipe(
request.header("Authorization"), //returns string | undefined
parseRawToken, //returns Either<Error, string>
TE.fromEither, //To TaskEither
TE.chain(validateUser), //validate token
TE.map(() => request) //If no errors return request
)
//Mock request
const mockRequest = {
header: (name: string) => "fake token",
body: {
userName: "MsFake",
}
} as Request;
//Dummy implementations for tokenValidators: sucess and fail
const mockValidToken: (token: string) => TE.TaskEither<Error, void> =
(token: string) => TE.right(void 0);
const mockInValidToken: (token: string) => TE.TaskEither<Error, void> =
(token: string) => TE.left(new Error("Token not valid"));
//Test
const fail = makeIsRequestAuthenticated(mockInValidToken)(mockRequest);
const sucess = makeIsRequestAuthenticated(mockValidToken)(mockRequest);
sucess().then(d => assert(d._tag === "Right"));
fail().then(d => assert(d._tag === "Left"));
Now the method MakeIsRequestAuthenticated returns a TaskEither<Error, Request> and you can chain it with any method taking a Request as a parameter. From now on you may deal with taskEither instead of promises, but it shouldn't be a problem.

Why am I am getting "Cannot find name 'function name' error"?

So I added this function below to my ts class:
private get(iD: string): Promise <Function> => async () => {
const tallConfig = await longCrudService.getHalf(iD);
const stream: Stream = tallConfig.stream;
const response = this.createUrl(tallConfig, stream);
return response;
}
But as soon as I add it, every other functions/Methods below it starts throwing this error: "Cannot find name 'function name'". Did I add it the wrong way?. I'm still trying to get a hold of typescript.
private get = async (id: string): Promise<Function> => {
or
private get: (iD: string) => Promise<Function> = async (iD: string) => {
What you are actually declaring here is a method. Declaring methods with arrow syntax goes like this:
private methodName = (param: ParamType): TypeOfReturnable => {
// remember to return something of type TypeOfReturnable
}

TypeScript error Argument of type '(response) => void' is not assignable to parameter of type '(value: void) => void | PromiseLike<void>'

I got type this problem with the following code:
"Argument of type '(response: IResponse) => void' is not assignable to parameter of type '(value: void) => void | PromiseLike'".
And problem this the check() function.
Could you please help me?
I've got really no ideas how to fix.
Thanks!
interface IResponse {
isChecked: boolean;
}
type TResponse = IResponse;
.....
function dispatchAsync<TResponsePayload>(dispatch: Dispatch, actionType: string, asyncCall: () => Promise<TResponsePayload>): any;
....
check = async () => {
const {actions, param} = this.props;
await actions.getInfoById(param.id).then(
(response: IResponse) => {
this.setState({isChecked: response.isChecked});
}
);
};
.....
getInfoById = async (id: string): Promise<void> =>
dispatchAsync<TPermission>(this.dispatch, CHECK_ACTION, async () => {
return await this.service.getInfoById(id);
});
To explain a little bit more, the TypeScript error tells you in another words that you cannot put a function that expects an argument in place of a function that doesn't expect any argument whatsoever.
Imagine code like this:
const myArg = (param: number) => setState(param + 5);
const caller = (arg: () => void) => arg();
caller(myArg);
Here is pretty obvious the caller won't provide any arguments when calling it's argument. Therefore calling the caller with myArg will result in an error thrown inside the myArg as it won't receive the argument it expects.
In your example the getInfoById returns Promise<void>. That means the then method will not provide any argument to the provided callback. That is the root cause.
You can fix it by telling the TypeScript that getInfoById actually returns something inside the promise (in this case I assume it is the IResponse type).
Like so:
getInfoById = async (id: string): Promise<IResponse> =>
dispatchAsync<TPermission>(this.dispatch, CHECK_ACTION, async () => {
return await this.service.getInfoById(id);
});
You can look at your code below:
getInfoById = async (id: string): Promise<void> =>
dispatchAsync<TPermission>(this.dispatch, CHECK_ACTION, async () => {
return await this.service.getInfoById(id);
});
You can change void in your Promise<void> with any or your spesific data type.
Because you can use return if you use void.

How to correctly use any and/or unknown for function arguments

We're trying to convert an event bus from JavaScript to TypeScript. What concerns us is this piece of code:
export function publish(eventName, ...args) {
if (!subscriptions[eventName]) { return }
for (const callback of subscriptions[eventName]) {
const result = callback(...args)
if (result === false) { break }
}
}
Translated to TypeScript:
// eslint-disable-next-line #typescript-eslint/no-explicit-any
type TCallBack = (...args: any) => void
const subscriptions: { [key: string]: TCallBack[] } = {}
export function publish(eventName: string, ...args: unknown[]) {
if (!subscriptions[eventName]) { return }
for (const callback of subscriptions[eventName]) {
callback(...args)
}
}
There are no errors but I'm not quite sure that we used the any and unknown type correctly for the arguments of callback. A callback can have one, multiple or no arguments at all and the argument can be of any type. Is this the correct way of using any and unknown>

Javascript Promise.all confusion

I am using Javascript/Typescript in order to format a password value on a collection of PersonModel objects. I am executing a Promise on each element in the Collection.
I am using Promise.all to allow all Promises to complete, and then want to return the formatted collection.
However, I get an error at build time.
PersonService.ts
private decryptPersons(persons: PersonModel[]): Promise<PersonModel[]> {
return new Promise<PersonModel[]>(resolve => {
let promises: Array<Promise<string>> = [];
let decryptedPersons: PersonModel[] = [];
for (let i: number = 0; i < persons.length; i++) {
let ciphertext: string = persons[i].password;
promises.push(this.decrypt(ciphertext).then((password: string) => { // <== line 357
persons[i].password = password;
decryptedPersons.push(persons[i]);
}));
}
Promise.all(promises).then(() => {
resolve(decryptedPersons);
});
});
}
private decrypt(value: string): Promise<string> {
return new Promise<string>(resolve => {
this.encrypter.decrypt(value).then((result: string) => {
resolve(result);
});
});
}
Error
ERROR in ./app/pages/service/personService.ts
(357,31): error TS2345: Argument of type 'Promise<void>' is not assignable to parameter of type 'Promise<string>'.
Type 'void' is not assignable to type 'string'.
I am no expert with Javascript, so my structure may me incorrect. If anyone can advise, I would appreciate it.
You're trying to push the following into your promises array:
this.decrypt(ciphertext).then((password: string) => { // <== line 357
persons[i].password = password;
decryptedPersons.push(persons[i]);
})
but your function here doesn't return anything, so this is going to evaluate to a Promise<void>.
You're also abusing the "explicit promise construction antipattern" in several places here.
Give this a try:
private decryptPersons(persons: PersonModel[]): Promise<PersonModel[]> {
return Promise.all(persons.map(person =>
this.decrypt(person.password)
.then(password => {
person.password = password;
return person;
});
));
}
private decrypt(value: string): Promise<string> {
return this.encrypter.decrypt(value);
}

Categories