Tenho um tipo como segue:
type EventType = {
event1: undefined,
event2: {
user: number
}
}
type EventName = keyof EventType;
Esse tipo é usado para digitar uma função logEvent:
function logEvent<T extends EventName>(eventName: T, extraData: EventType[T]) {
}
Isso garante que, quando logEvent for chamado, a combinação correta de eventName e extraData seja aplicada:
logEvent('event1', undefined)
logEvent('event2', { user: 5 })
Entretanto, para o primeiro caso (indefinido), gostaria que o segundo argumento fosse opcional:
logEvent('event1')
logEvent('event2', { user: 5 })
Estou lutando para encontrar uma solução para isso. Minhas tentativas são, na maioria, sem sentido.
Talvez você não queira uma função genérica. Em vez disso, você pode querer uma função em que o tipo de parâmetro seja uma união de tuplas que representam todas as combinações possíveis de argumentos que você permite.
Então você quer um tipo como este:
E agora você usa esse tipo na sua
logEvent
função assim:Agora, cada nome de evento é pareado com os outros argumentos que o nome de evento específico precisa.
Veja o parque infantil
Então, como você gera esse
EventArgs
tipo a partir deEventType
?Com um tipo mapeado que mapeia sobre cada propriedade de
EventType
e cria a tupla necessária para cada uma. Poderia parecer assim:Isso mapeia sobre cada propriedade e verifica se o valor dessa propriedade
undefined
. Se forundefined
, então a tupla é apenas o nome da chave como o único argumento para a função. Se não for indefinido, use uma tupla com 2 membros, um para o nome da chave e um para o extraData.Veja o parque infantil
Infelizmente, o TypeScript não trata automaticamente parâmetros finais que aceitam
undefined
como opcionais . Há uma solicitação de recurso para isso em microsoft/TypeScript#12400 , mas não faz parte da linguagem desde o TypeScript 5.6.Se você quiser um comportamento como esse, precisará escrevê-lo você mesmo. Há várias maneiras de fazer isso, supondo que você queira manter isso como uma função genérica . Aqui estão duas:
Uma abordagem é usar um tipo condicional para operar no tipo tuple de parâmetros finais. Aqui está uma maneira de fazer isso:
O tipo mapeado
{[I in keyof T]: undefined}
pareceT
mas com cada elementoundefined
. Então seT
for[x: string | undefined, y: number]
, então o tipo mapeado parece[x: undefined, y: undefined]
. O tipo condicional verifica se esse tipo mapeado é atribuível aT
. Se for, então a coisa toda pode muito bem ser opcional, como representado porPartial<T>
. Se não for, então não pode ser opcional (pelo menos não a coisa toda). Então se fizermos isso com sua função, obtemos:Onde a
[extraData: EventType[K]]
tupla restante será transformada[extraData?: EventType[K]]
quandoEventType[K]
aceitarundefined
. Vamos testar:Ou você pode escrever sobrecargas em vez disso, se não quiser mexer com tuplas rest. Você ainda precisa de algum tipo de tipo condicional para calcular quais coisas são opcionais. Para seu código, pode parecer:
Aqui
UndefinableKeys<T>
retorna o subconjunto das chaves deT
for queundefined
é atribuível à propriedade correspondente. Então você pode simplesmente fazer duas assinaturas de chamada, uma para cada maneira de chamá-la:Link do playground para o código
Você pode usar parâmetros de descanso condicionais:
Parque infantil