Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strange error with --strictFunctionTypes #19746

Closed
pelotom opened this issue Nov 5, 2017 · 1 comment
Closed

Strange error with --strictFunctionTypes #19746

pelotom opened this issue Nov 5, 2017 · 1 comment
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@pelotom
Copy link

pelotom commented Nov 5, 2017

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,
@ghost ghost added the Bug A bug in TypeScript label Nov 5, 2017
@ahejlsberg
Copy link
Member

ahejlsberg commented Nov 6, 2017

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.

@ahejlsberg ahejlsberg self-assigned this Nov 6, 2017
@ahejlsberg ahejlsberg added this to the TypeScript 2.7 milestone Nov 6, 2017
@ahejlsberg ahejlsberg added the Fixed A PR has been merged for this issue label Nov 6, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

2 participants