winstonjs/logform

[Bug]: Incorrect typing in TransformableInfo.message

mazamachi opened this issue · 3 comments

The problem

Current typing of TransformableInfo is

export interface TransformableInfo {
  level: string;
  message: string;
  [key: string]: any;
}

logform/index.d.ts

Lines 7 to 11 in 0062e43

export interface TransformableInfo {
level: string;
message: string;
[key: string]: any;
}

However, message property can be non-string object in winston.
https://github.com/winstonjs/winston/blob/master/lib/winston/logger.js#L221-L227

Winston declares message as any in TypeScript.
https://github.com/winstonjs/winston/blob/master/index.d.ts#L81

So the message of TransformableInfo.message should be more loose, e.g. any.

Example

This is problematic when writing custom format function. Like

const winston = require('winston');
const logger = winston.createLogger({
    level: 'info',
    format: winston.format.combine(
        winston.format((info) => ({
            ...info,
            message: info.message.trim(), // compiles as the `message` type is `string`, but actually it can be `object` or `any`.
        }))(),
        winston.format.simple(),
    ),
    transports: [new winston.transports.Console()],
});
logger.info('      message\t'); // OK. logs `'info: message'`
logger.info({ foo: 'bar' }); // NG. throws error
// Uncaught TypeError: info.message.trim is not a function
//     at Format.transform (...)
//     at Format.transform (/.../node_modules/logform/combine.js:20:24)
//     at DerivedLogger._transform (/.../node_modules/winston/lib/winston/logger.js:313:29)

And if a user transforms object properly, typescript cannot compile.

const winston = require('winston');
const logger = winston.createLogger({
    level: 'info',
    format: winston.format.combine(
        winston.format((info) => ({
            ...info,
            message: typeof info.message === 'string' ? 
                info.message.trim() 
                : info.message.toString(),
                // ^^^ info.message is `never` here, so toString() does not exist
        }))(),
        winston.format.simple(),
    ),
    transports: [new winston.transports.Console()],
});
logger.info('      message\t'); // works. logs `'info: message'`
logger.info({ foo: 'bar' }); // works. logs `'info: [object Object]'`

Actually, the example code above cannot be compiled in TypeScript, it can run properly as JavaScript.

What version of Logform presents the issue?

2.4.0

What version of Node are you using?

v14.17.0

If this worked in a previous version of Logform, which was it?

No response

Minimum Working Example

(Same as examples above)

const winston = require('winston');
const logger = winston.createLogger({
    level: 'info',
    format: winston.format.combine(
        winston.format((info) => ({
            ...info,
            message: info.message.trim(),
        }))(),
        winston.format.simple(),
    ),
    transports: [new winston.transports.Console()],
});
logger.info('      message\t'); // OK. logs `'info: message'`
logger.info({ foo: 'bar' }); // NG. throws error
// Uncaught TypeError: info.message.trim is not a function
//     at Format.transform (...)
//     at Format.transform (/.../node_modules/logform/combine.js:20:24)
//     at DerivedLogger._transform (/.../node_modules/winston/lib/winston/logger.js:313:29)
const winston = require('winston');
const logger = winston.createLogger({
    level: 'info',
    format: winston.format.combine(
        winston.format((info) => ({
            ...info,
            message: typeof info.message === 'string' ? 
                info.message.trim() 
                : info.message.toString(),
                // ^^^ info.message is `never` here, so toString() does not exist
        }))(),
        winston.format.simple(),
    ),
    transports: [new winston.transports.Console()],
});
logger.info('      message\t'); // works. logs `'info: message'`
logger.info({ foo: 'bar' }); // works. logs `'info: [object Object]'`

Additional information

No response

🔎 Search Terms

typescript, message, typing

I just ran into this when trying to create a custom transport for winston in TS. the actual formatted message is stored in a key on the object that is a symbol type, not a string, so TS complains about unique symbols not being allowed as an indexer in this case.

wbt commented

PR welcome!

Will do