镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Strange error with --strictFunctionTypes #19746

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,

Activity

  1. ghost added
    BugA bug in TypeScript
    on Nov 5, 2017
  2. ahejlsberg commented on Nov 6, 2017

    @ahejlsberg
    Member

    This appears to be an issue with the improved error elaboration logic for invariant generic types (discussed here #18654 (comment)), which apparently isn't so improved in this case. With --strictFunctionTypes, the expected behavior in your example is an error but we're not consistently reporting it. BTW, the error looks to be caused by Runtype<A> and Constraint<A> both being invariant for A because Constraint<A> is using A in both co- and contra-variant positions.

  3. locked and limited conversation to collaborators on Jun 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

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