// I define the callback function interface for the .map() method. This entire interface
// is parameterized with the given Type <T> so that we can facilitate "type argument
// inference" in the method signature for .map().
interface TokensMapCallback<T> {
	( token: string ) : T;
}

class Tokens {

	private _tokens: string[];

	// I initialize the tokens class with the given tokens.
	constructor( tokens: string[] ) {

		this._tokens = tokens;

	}

	// ---
	// PUBLIC METHODS.
	// ---

	// I map the internal tokens using the given callback.
	// --
	// NOTE: By using <T> to parameterize both the method and the callback type, we
	// allow for "type argument inference", which is when TypeScript looks at the method
	// argument and uses its Type as way to automatically determine which type should be
	// used to parameterize the map<T> method. In this case, it will look at the return-
	// type of the callback as the parameterization type (as defined in TokensMapCallback).
	public map<T>( callback: TokensMapCallback<T> ) : T[] {

		return( this._tokens.map( callback ) );

	}

	// I map the internal tokens using the given callback.
	// --
	// NOTE: This has the same effect as the previous map<T> method, except for this uses
	// an in-line type definition for the callback. You can still see that the callback
	// is defined to return T, which can then allow for "type argument inference" for the
	// parameterization of .map2<T>.
	public map2<T>( callback: ( token: string ) => T ) : T[] {

		return( this._tokens.map( callback ) );

	}

}

// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //

var tokens = new Tokens( [ "Sarah", "Joanna", "Tricia" ] );

// Let's map the tokens using a Callback that returns a String value.
// --
// NOTE: The :string return type of the callback will be used to infer the return
// type of the .map() method.
var greetings: string[] = tokens.map(
	( token: string ) : string => {

		return( `Hello, ${ token }.` );

	}
);

for ( var greeting of greetings ) {

	console.log( greeting );

}

// Let's map the tokens using a Callback that returns a Boolean value.
// --
// NOTE: The :boolean return type of the callback will be used to infer the return
// type of the .map2() method.
var checks: boolean[] = tokens.map2(
	( token: string ) : boolean => {

		return( token === "Joanna" );

	}
);

for ( var check of checks ) {

	console.log( check );

}