Eu te diria que esse esforço é inútil. No final das contas tudo será javascript e os tipos irão desaparecer no momento que a aplicação estiver rodando. A tipagem do Typescript não faz nenhuma validação de dados e é perigoso acreditar nisso.
Basicamente o que você quer é garantir que se a função receber um parâmetro X, só sejam válidas as chaves B e, se receber um parâmetro Y, só serão válidas as chaves C. Contudo fazer isso com typescript é um esforço inútil, já que no momento em que o programa estiver rodando você poderá enviar o Parâmetro X com uma chave B sem nenhum tipo de erro ou problema.
Por isso falei em utilizar libs de validação como o zod. A solução que você quer ficaria mais ou menos assim com o zod:
import { z } from 'zod'
const schema = z.object({
// campos do objeto aqui
}).superRefine(() => {
// lógica que você quer aqui
)
type seuTipo = z.infer<typeof schema>
Para ficar mais simples de entender vou dar como exemplo o caso onde eu utilizei essa solução:
import { z } from 'zod'
export const EditUserDTOSchema = z
.object({
id: z.string({ required_error: 'user id is required' }).uuid(),
name: z
.string()
.nonempty({ message: 'user name cannot be empty' })
.max(30, { message: 'user name max length is 30' })
.optional(),
lastName: z
.string()
.nonempty({ message: 'user name cannot be empty' })
.max(100, { message: 'user name max length is 100' })
.optional(),
password: z
.string()
.min(8, { message: 'user password min length is 8' })
.max(60, 'user password max length is 60')
.optional()
})
.superRefine((val, ctx) => {
if (!val.name && !val.lastName && !val.password)
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'At least one user optional key must be provided',
fatal: true
})
})
export type EditUserDTO = z.infer<typeof EditUserDTOSchema>
Nesse caso eu queria fazer com que pelo menos um dos parâmetros opcionais fossem obrigatórios na hora de receber o objeto, parecido com o que você está querendo. O ChatGPT me deu uma solução idêntica a que você utiliza no tipo RequireOnlyOne
, mas não funcionava.
A vantagem é que dessa forma eu não tenho apenas o tipo, mas também uma forma de validar diretamente esses erros, onde, caso eu não receba nenhum dos três parâmetros opcionais, um erro será retornado, que é o comportamento que eu quero.
Talvez exista uma forma mais simples de resolver o problema da tipagem com intersection types ou union tipes, mas é como eu disse no início, no final esse esforço vai ser inútil.