Você está confundindo níveis de abstração. Como outras respostas já apontam,
CREATE TYPE apenas registra um tipo (composto/linha) no sistema. Enquanto um ROW construtor realmente retorna uma linha. Um tipo de linha criado com o
ROW construtor não preserva os nomes das colunas, o que fica evidente quando você tenta converter a linha em JSON. Enquanto isso,
ROW é apenas uma palavra de ruído a maior parte do tempo. A documentação:
Demonstração:
SELECT t AS r1, row_to_json(t) AS j1
, ROW(1, 'x', NUMERIC '42.1') AS r2, row_to_json(ROW(1, 'x', NUMERIC '42.1')) AS j2
, (1, 'x', NUMERIC '42.1') AS r3, row_to_json( (1, 'x', NUMERIC '42.1')) AS j3
, (1, 'x', '42.1')::myrowtype AS r4, row_to_json((1, 'x', '42.1')::myrowtype) AS j4
FROM (SELECT 1, 'x', NUMERIC '42.1') t;
db<>fiddle aqui
sqlfiddle
r1 e j1 preserve os nomes das colunas originais.r2 e j2 não.r3 e j3 são os mesmos; para demonstrar como ROW é apenas ruído.r4 e j4 carregam os nomes das colunas do tipo registrado. Você pode converter a linha (registro) em um tipo de linha registrado se número e tipos de dados dos elementos correspondem ao tipo de linha - nomes dos campos de entrada são ignorados.