Reputation: 28553
I would like to have a type that ensures an object has a type of A
or B
or A and B
. However one of the cases that I think should fail is not. I'm sure it's something stupid I just can't see it yet.
interface ValueSelector
{
type: "id" | "value_string"
value: string
}
interface TemporalSelector
{
id: number
}
type Selector = (ValueSelector & TemporalSelector) | ValueSelector | TemporalSelector
// Should error
const e0: Selector = {}
const e1: Selector = { id: 0, value: "" } // <-- does not error
const e2: Selector = { type: "id" }
const e3: Selector = { type: "value_string" }
const e4: Selector = { value: "" }
const e5: Selector = { value: "" }
// Should pass
const a1: Selector = { id: 0 }
const a2: Selector = { type: "id", value: "" }
const a3: Selector = { type: "value_string", value: "" }
const a4: Selector = { id: 0, type: "id", value: "" }
const a5: Selector = { id: 0, type: "value_string", value: "" }
Upvotes: 1
Views: 129
Reputation: 33091
e1
does not trigger an error because { id: 0, value: "" }
is already assignable to TemporalSelector
since it expects only id
property.
In order to make it work you can use the StrictUnion
helper:
interface ValueSelector {
type: "id" | "value_string"
value: string
}
interface TemporalSelector {
id: number
}
type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> =
T extends any
? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>
type Selector = (ValueSelector & TemporalSelector) | StrictUnion<ValueSelector | TemporalSelector>
// Should error
const e0: Selector = {}
const e1: Selector = { id: 0, value: "" } // error
const e2: Selector = { type: "id" }
const e3: Selector = { type: "value_string" }
const e4: Selector = { value: "" }
const e5: Selector = { value: "" }
// Should pass
const a1: Selector = { id: 0 }
const a2: Selector = { type: "id", value: "" }
const a3: Selector = { type: "value_string", value: "" }
const a4: Selector = { id: 0, type: "id", value: "" }
const a5: Selector = { id: 0, type: "value_string", value: "" }
Upvotes: 2