I have a function that accepts the types defined in A as an argument, but I want to make the typing more restrictive so I created B.
interface A {
readonly x: any;
}
interface B extends Omit<A, 'x'> {
readonly x: number;
}
The problem is, this function won't accept the types in B, even though it's more restrictive than A. Instead I get the error No overload matches this call
, saying that Type(props: B) => number is not assignable to type (props: A) => number"
.
This function also won't accept props: { x : number }
, giving me a similar error message.
I have a function that accepts the types defined in A as an argument, but I want to make the typing more restrictive so I created B.
interface A {
readonly x: any;
}
interface B extends Omit<A, 'x'> {
readonly x: number;
}
The problem is, this function won't accept the types in B, even though it's more restrictive than A. Instead I get the error No overload matches this call
, saying that Type(props: B) => number is not assignable to type (props: A) => number"
.
This function also won't accept props: { x : number }
, giving me a similar error message.
- 1 Can you share a plete example? The code snippet does not give an error by itself. – Vojtěch Strnad Commented Jul 12, 2021 at 10:01
2 Answers
Reset to default 3The problem is that function arguments are contravariant. And their assignability is checked in the reverse direction pared to normal value types assignability. That's why you cannot assign a function accepting limited subset of values to the function accepting much wider set of values:
interface A {
readonly x: unknown;
}
interface B extends Omit<A, 'x'> {
readonly x: number;
}
declare function narrowerFn(props: B): number
const widerFn: (props: A) => number = narrowerFn // errror
playground link
It makes a perfect sense. Since the target side function can accept for example a string that source side function doesn't know how to deal with.
But it perfectly works in the opposite direction:
interface A {
readonly x: string;
}
interface B extends Omit<A, 'x'> {
readonly x: number | string;
}
declare function widerFn(props: B): number
const narrowerFn: (props: A) => number = widerFn
playground link
Since now the target side function can accept only a subset of values the source side function can deal with perfectly.
First of all, you do not need Omit
. Just override the property as follows:
interface A {
readonly x: any;
}
interface B extends A {
readonly x: number;
}
Also, I've created the following example to highlight the problem:
TS Playground
type FA = (props: A) => number;
type FB = (props: B) => number;
const fb: FB = ({ x }) => x + 1;
const fa: FA = fb;
// Unsafe usage, cause fb expects numbers, not strings
console.log(fa({ x: '1' })); // Output "11" instead of 2