Skip to content

Strange error with --strictFunctionTypes #19746

Closed
@pelotom

Description

@pelotom

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 or Constraint to be a type instead of an interface, e.g.
    type Num = Runtype<number> & {
      tag: 'number'
    }
  • delete any of the fields: tag, underlying or check
  • change the signature of check to
    check(x: A['witness']): void,

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFixedA PR has been merged for this issue

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions