I have an object that looks like this:
export declare interface Logger {
info(msg: string, options?: LogOptions): void;
warn(msg: string, options?: LogOptions): void;
warnOnce(msg: string, options?: LogOptions): void;
error(msg: string, options?: LogOptions): void;
clearScreen(type: LogType): void;
hasErrorLogged(error: Error | RollupError): boolean;
hasWarned: boolean;
}
I want to create a new object that has all the same properties but with altered info
, warn
, warnOnce
and error
functions. The changes to each function are the same.
I tried the following but TypeScript says Element implicitly has an 'any' type because expression of type 'string | number | symbol' can't be used to index type
:
const alterMsg = (msg: string) => msg.replaceAll('A', 'B');
const alterOptions = {
flag: false,
};
const logger: Logger = createLogger();
const { clearScreen, hasErrorLogged, hasWarned, ...functionsToAlter } = logger;
Object.keys(functionsToAlter).forEach(key => {
const originalFunction = functionsToAlter[key as keyof Partial<Logger>];
const alteredFunction = (msg: string, options?: LogOptions) =>
originalFunction(alterMsg(msg), {
...alterOptions,
...options,
});
functionsToAlter[key as keyof Partial<Logger>] = alteredFunction;
});
const newLogger: Logger = {
...functionsToChange,
...logger,
};
The type should stay the same. It is still the same object structure and each of the functions still accepts a string msg
and an optional options
object.
CodePudding user response:
Kind of partial solution:
Based on this answer https://stackoverflow.com/a/50593801/5089567 typing destructuration helps infer functionsToChange
's type
const { clearScreen, hasErrorLogged, hasWarned, ...functionsToChange }: Logger & Partial<Logger> = logger;
Then instead of
key as keyof Partial<Logger>
use
key as keyof Omit<Logger, 'clearScreen' | 'hasErrorLogged' | 'hasWarned'>
And finally
const newLogger: Logger = {
clearScreen, hasErrorLogged, hasWarned,
...functionsToChange,
};
Works fine in playground