xenoterracide xenoterracide - 3 months ago 13
TypeScript Question

Why is this not a function

For some reason

authenticationProvider
seems to be missing.

@autoinject()
export class ProviderManager implements AuthenticationManager {
constructor( private container: Container ){
}
public authenticate( creds: Credentials ): Promise<Authentication> {
var provider = creds.authenticationProvider();
return this.container.get( provider ).authenticate( creds );
}

}


Credentials


export interface Credentials {
authenticationProvider(): { new(): AuthenticationManager };
}


the implementation
UsernamePasswordCredentials


export class UsernamePasswordCredentials implements Credentials {

public authenticationProvider(): {new(): AuthenticationManager} {
return HttpBasicAuthentication;
}

user: String;
pass: String;
}


AuthenticationManager


export interface AuthenticationManager {
authenticate( creds: Credentials ): Promise<Authentication>;
}


Login


@autoinject()
export class Login {
private log: Logger = LogManager.getLogger( Login );
creds: UsernamePasswordCredentials;

constructor( private am: AuthenticationManager, private router: Router ) {
}

public signIn(): void {
this.log.debug( `authenticating ${ this.creds.user }` );
this.am.authenticate( this.creds ).then( auth => {
var route = Route.MANAGE_CONTENT;
this.log.debug( `navigating to ${ route }` );
var router = this.router;
router.navigate( route );
} ).catch( error => {
this.log.error( error );
} );
}
}


Here's Chrome's stacktrace

VM802:1 Uncaught TypeError: creds.authenticationProvider is not a function(…)(anonymous function) @ VM802:1
authenticate @ ProviderManager.ts:13
signIn @ login.ts:22evaluate @ aurelia-binding.js:1522
callSource @ aurelia-binding.js:4963
(anonymous function) @ aurelia-binding.js:4987
handleDelegatedEvent @ aurelia-binding.js:3176

Answer

Thanks for sharing the Login code.

You're on the right lines, but it appears that this.creds was previously undefined.

The code

class Login {
    creds: UsernamePasswordCredentials;
}

just tells Typescript that it expect creds to be of type UsernamePasswordCredentials. But it does nothing to initialize the creds field... in particular, no javascript will get emitted for the line when the above gets transpiled (https://www.typescriptlang.org/play/ might be helpful to look at what the transpiler does).

That means that, in the signIn method, this.creds will be undefined.

This is similar to, say, in C#, where if you don't initialize a field, it defaults to null.

Whilst Typescript can catch a lot of things, it cannot (always) catch when a variable has an undefined/null value at run-time, just as a statically typed language like C# can still end up generating compiled code which triggers null reference exceptions.

EDIT: Ahh, my apologies. Had creds been undefined, you would have received a ReferenceError: creds is not defined. The TypeError should have made clear creds was populated elsewhere, but of the wrong type.

Comments