promisify меняет сигнатуру функции с условного
function func<T>(...args: any[], cb: (err: any, result: T) => void): any;
// или
function func<T1, T2>(...args: any[], cb: (err: any, res1: T1, res2: T2) => void): any;
на
function func<T>(...args: any[]): Promise<T>;
// или
function func<T1, T2>(...args: any[]): Promise<{res1: T1; res2: T2}>;
Для exec это возможно, так как его сигнатура подходит под 2 вариант:
function exec(command: string, options?: ExecOptions, cb: (err: Error, stdout: string | Buffer, stderr: string | Buffer) => void): ChildProcess;
что приходит к сигнатуре:
function exec(command: string, options?: ExecOptions): Promise<{stdout: string | Buffer; stderr: string | Buffer}>;
Для spawn этого сделать нельзя, ибо сигнатура не подходит:
function spawn(command: string, args: string[], options: SpawnOptions): ChildProcess;
Просто некуда подставить колбэк, который будет отслеживать промис