I encounter Type 'unknown' is not assignable to type 'number'
when trying to implement something like this:
interface Foo<V = unknown> {
foo: (value: V) => void
}
class Bar implements Foo<number> {
foo(v: number) {}
}
function test(foo: Foo): void {}
test(new Bar) // complains:
// Argument of type 'Bar' is not assignable to parameter of type 'Foo<unknown>'.
// Types of property 'foo' are incompatible.
// Type '(v: number) => void' is not assignable to type '(value: unknown) => void'.
// Types of parameters 'v' and 'value' are incompatible.
// Type 'unknown' is not assignable to type 'number'.(2345)
Why does it behave like this and how can I redesign this considering that test
should be able to get different implementations of Foo
, e.g.
class Bar2 implements Foo<string> {
foo(v: string) {}
}
Type assertion:
test(new Bar as Foo) // it doesn't complain now, but imo it's dirty and bad DX workaround
Tried to replace unknown
in Foo
with union type e.g. string | number | ...
, but it didn't help.
Affectively the main issue here is unknown
cannot be assigned to anything but itself and the any
type.
If you strip down your example more
type FooFn = (value: unknown) => void
const testFooFn: FooFn = (n: number) => {} // Error
You get a similar error to the one you get above.
Type '(n: number) => void' is not assignable to type 'FooFn'.
Types of parameters 'n' and 'value' are incompatible.
Type 'unknown' is not assignable to type 'number'.
One option, as suggested in the comments, is to use generics to carry over the type.
interface Foo<V = unknown> {
foo: (value: V) => void
}
class Bar implements Foo<number> {
foo(v: number) {
console.log(v)
}
}
function test<T>(foo: Foo<T>): void {}
test(new Bar()) // No Error
Another option is change unknown
to any
. See the differences here, but typically it's better/safer to use unknown
but requires more type gymnastics.
interface Foo<V = any> {
foo: (value: V) => void
}
class Bar implements Foo<number> {
foo(v: number) {
console.log(v)
}
}
function test(foo: Foo): void {}
test(new Bar()) // No Error
See TSPlayground