Description
TypeScript Version: 2.6.1
Code
This snippet is a based on a problem discovered when using my library, runtypes
, with TypeScript 2.6 and --strictFunctionTypes
. I've stripped it down to the following minimal repro:
const Foo = Obj({ foo: Num })
// ^^^^^^^^^^^^
// error TS2345: Argument of type '{ foo: Num; }' is not assignable to parameter of type '{ [_: string]: Runtype<any>; }'.
// Property 'foo' is incompatible with index signature.
// Type 'Num' is not assignable to type 'Runtype<any>'.
// Types of property 'constraint' are incompatible.
// Type 'Constraint<Num>' is not assignable to type 'Constraint<Runtype<any>>'.
// Types of property 'underlying' are incompatible.
// Type 'Num' is not assignable to type 'Runtype<any>'.
interface Runtype<A> {
constraint: Constraint<this>
witness: A
}
interface Num extends Runtype<number> {
tag: 'number'
}
declare const Num: Num
interface Obj<O extends { [_ in string]: Runtype<any> }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {}
declare function Obj<O extends { [_: string]: Runtype<any> }>(fields: O): Obj<O>;
interface Constraint<A extends Runtype<any>> extends Runtype<A['witness']> {
underlying: A,
check: (x: A['witness']) => void,
}
Expected behavior:
Should type check.
Actual behavior:
It fails with the error shown in the comment.
Commentary
The error is strange; it begins and ends with this:
Type 'Num' is not assignable to type 'Runtype<any>'
But that's clearly not true, as evidenced by the fact that this works:
const wat: Runtype<any> = Num
And bizarrely, if you make that declaration at the top of the file it will make the error go away! But only if you declare wat
before Foo
. If you declare wat
after Foo
, both Foo
and wat
are flagged with Type 'Num' is not assignable to type 'Runtype<any>'.
There are a number of other ways to make the error go away, none of which I understand any better:
- change any of
Num
,Obj
orConstraint
to be atype
instead of aninterface
, e.g.type Num = Runtype<number> & { tag: 'number' }
- delete any of the fields:
tag
,underlying
orcheck
- change the signature of
check
tocheck(x: A['witness']): void,