Solución:
Creo que lo descubrí. En knex.js, digamos que especifica una tabla como:
knex.select( '*' ).from( 'Users' )
Luego, puede agregar la palabra clave AS dentro de las comillas del nombre de la tabla para crear un alias, así:
knex.select( '*' ).from( 'Users AS u' )
..y también puede hacer esto para los nombres de las columnas; entonces mi SQL original se vería así en knex-land:
knex.select( 'w.*', 'ua.name AS ua_name', 'uw.name AS uw_name' )
.innerJoin( 'Users AS ua', 'author_id', 'ua.id' )
.leftJoin( 'Users as uw', 'winner_id', 'uw.id' )
Supongo que me confundió la presencia del método .as () de knex, que (hasta donde tengo entendido actualmente) está destinado solo a subconsultas, no a tablas de alias o nombres de columnas.
Hay dos formas de declarar un alias para el identificador (tabla o columna). Se puede dar directamente como sufijo aliasName para el identificador (por ejemplo, identifierName como aliasName) o se puede pasar un objeto {aliasName: ‘identifierName’}.
Entonces, el siguiente código:
knex.select('w.*', 'ua.name', 'uw.name')
.from({ w: 'Words' })
.innerJoin({ ua: 'Users' }, 'w.author_id', '=', 'ua.id')
.leftJoin({ uw: 'Users' }, 'w.winner_id', '=', 'uw.id')
.toString()
se compilará para:
select "w".*, "ua"."name", "uw"."name"
from "Words" as "w"
inner join "Users" as "ua" on "w"."author_id" = "ua"."id"
left join "Users" as "uw" on "w"."winner_id" = "uw"."id"