Reputation: 24567
I have a function which should be able to receive two similar types as argument. Depending on if props.multi
is true
or false
.
The problem is that it still allows me to pass value = []
with multi = false
. How can I get rid of this?
interface PropsSingle<T> {
value: T,
multi: false
}
interface PropsMulti<T> {
value: T[],
multi: true
}
function foo<T>(props: PropsSingle<T> | PropsMulti<T>) {
if(props.multi) {
let arr = props.value // type detection works fine
} else {
let obj = props.value // type detection works fine
}
}
foo({multi: true, value: []}) // works as expected
foo({multi: true, value: {}}) // throws error as expected
foo({multi: false, value: {}}) // works as expected
foo({multi: false, value: []}) // THIS SHOULD THROW AN ERROR
I already have tried foo<T extends object>(...)
and foo<T extends {[key: string]: any}>(...)
The only kinda solution / workaround I found is this:
interface PropsSingle<T> {
value: T extends any[] ? never : T,
multi: false
}
But that looks weird to me.
Why can't I restrict the type at function foo
?
Upvotes: 2
Views: 2840
Reputation: 250136
The reason pretty simple, there is nothing preventing T
in PropsSingle
to be any[]
. And there is no simple way to let typescript know that a generic parameter can't extends a given type, we can only specify what T
extends.
The solution you have should work, I usually add the constraint on the function and use a string literal type to offer a more sugestive error message when dealing with this kind of scenario:
interface PropsSingle<T> {
value: T,
multi: false
}
interface PropsMulti<T> {
value: T[],
multi: true
}
type ErrorIf<T, U, TError> = T extends U ? TError : {}
function foo<T>(props: (PropsSingle<T> & ErrorIf<T, any[], "Argument to PropsSingle can't be []")| PropsMulti<T>) {
if(props.multi) {
let arr = props.value // type detection works fine
} else {
let obj = props.value // type detection works fine
}
}
foo({multi: true, value: []}) // works as expected
foo({multi: true, value: {}}) // throws error as expected
foo({multi: false, value: {}}) // works as expected
foo({multi: false, value: []}) // Error Type '{ multi: false; value: undefined[]; }' is not assignable to type '"Argument to PropsSingle can't be []"'.
Upvotes: 2