Учитывая это StrictUnion
generi c:
type UnionKeys<T> = T extends T? keyof T : never;
type StrictUnionHelper<T, TAll> = T extends T? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, undefined>> : never;
export type StrictUnion<T> = StrictUnionHelper<T, T>
type Working = StrictUnion<(
{ id: number } |
{ uuid: string } |
{ memberId: number } |
{ memberUuid: string } |
{ member: Member } |
{ organizationId: number, contactId: number })
>
const a: Working = { id: 1 }
const b: Working = { uuid: '123456' }
const c: Working = { organizationId: 1, contactId: 1 }
const d: Working = { organizationId: 1 }
const e: Working = { id: 1, organizationId: 1, contactId: 1 } // I would like this to work
const f: Working = { }
Я бы хотел StrictUnion
, чтобы разрешить "дополнительные" свойства. Это означает, что это должно работать:
{ id: 1, organizationId: 1, contactId: 1 }
Я ищу совпадения, чтобы соответствовать хотя бы одному из союзов, но также допускаю дополнительные свойства, то есть вы не сможете просто пропустить organizationId
потому что ему нужно contactId
, но вы сможете пройти только id
, но не empty
{}
.
Проблема, если вы используете регулярное объединение, заключается в следующем: если есть способ использовать регулярное объединение и решить эти проблемы, я бы принял это как решение.
async member(props: (
{ id: number } |
{ uuid: string } |
{ memberId: number } |
{ memberUuid: string } |
{ member: Member } |
{ organizationId: number, contactId: number })
) {
const member = await (async () => {
if (props.member) return member
if (props.id) return this.memberRepository.readOneByIdOrFail(props.id)
if (props.memberId) return this.memberRepository.readOneByIdOrFail(props.memberId)
if (props.uuid) return this.memberRepository.readOneByUuidOrFail(props.uuid)
if (props.memberUuid) return this.memberRepository.readOneByUuidOrFail(props.memberUuid)
if (props.organizationId && props.contactId) return this.memberService.readByUnique(props)
throw new Error(`unable to ${MemberService.name}.member`)
})()
return { member }
}
Больше мыслей:
type Requirements = (
{ id: number } |
{ uuid: string } |
{ memberId: number } |
{ memberUuid: string } |
{ member: Member } |
{ organizationId: number, contactId: number }
)
✅ Should be valid
❌ Should not be valid
const a: Requirements = { id: 1 } ✅
const b: Requirements = { uuid: '123456' } ✅
const c: Requirements = { id: null, uuid: null } ✅ (will throw, but valid)
const d: Requirements = { uuid: '123456', contactId: 1 } ✅
const e: Requirements = { organizationId: 1, contactId: 1 } ✅
const f: Requirements = { uuid: '12345', organizationId: 1, contactId: 1 } ✅
const g: Requirements = { uuid: null, organizationId: 1, contactId: 1 } ✅
const h: Requirements = { organizationId: 1 } ❌
const i: Requirements = { id: 1, organizationId: undefined } ✅
const j: Requirements = { } ❌
Мне нравится то, что делает AtLeastOne
:
type AtLeastOne<T, U = {[K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U]
Что мешает пустому объекту {}
AtLeastOne<{
id: number,
uuid: string,
memberId: number,
memberUuid: string,
member: Member,
organizationId: number,
contactId: number
}>
Я просто хочу, чтобы organizationId
или contactId
были недействительными в одиночку, потому что они - пара.
Вот некоторые идеи имен для того, что я хочу PartialPairs
или AtLeastOnePair
:
PartialPairs<
{ id: number } |
{ uuid: string } |
{ memberId: number } |
{ memberUuid: string } |
{ member: Member } |
{ organizationId: number, contactId: number }
>
AtLeastOne<{
{ id: number } |
{ uuid: string } |
{ memberId: number } |
{ memberUuid: string } |
{ member: Member } |
{ organizationId: number, contactId: number }
}>