Skip to content

Interface with methods interferes with string literal type inference #19497

Closed

Description

TypeScript Version: 2.7.0-dev.20171026

Minimal example:

interface Observable<T> {peek(): T}
declare function obs<T>(val: T): Observable<T>
const t: Observable<'A'> = obs('A')

Expected behavior:
Compilation without errors.

Actual behavior:

src/test.ts(3,7): error TS2322: Type 'Observable<string>' is not assignable to type 'Observable<"A">'.
  Type 'string' is not assignable to type '"A"'.

This error goes away if the interface has no methods or function signature:

interface Observable<T> {}

Alternately, you can redundantly specify the type of the argument to obs():

const t: Observable<'A'> = obs('A' as 'A')

Activity

  1. ghost added
    BugA bug in TypeScript
    on Oct 26, 2017
  2. ahejlsberg commented on Oct 26, 2017

    @ahejlsberg
    Member

    This is a design limitation and is working as intended. See #10676 for context. In the original example we can't tell that T is used only in output positions within Observable<T>. Therefore we widen from 'a' to string. As you point out, you can force the desired result by writing 'a' as 'a'. It would be nice to do better here, but it isn't clear how we could cheaply and consistently determine that a type parameter is used only in output positions.

  3. added
    Design LimitationConstraints of the existing architecture prevent this from being fixed
    and removed
    BugA bug in TypeScript
    on Oct 26, 2017
  4. ahejlsberg commented on Oct 26, 2017

    @ahejlsberg
    Member

    Andy (Andrewkraft) (@Andy-MS) Your example is working as it should. Array elements are mutable locations so widening is to be expected.

  5. kpdonn commented on Nov 1, 2017

    @kpdonn
    Contributor

    Wolfgang Faust (@wolfgang42) Another potential workaround for your example if you are only expecting strings is:

    interface Observable<T extends string> {peek(): T}
    declare function obs<T extends string>(val: T): Observable<T>
    const t: Observable<'A'> = obs('A') // now works
  6. wolfgang42 commented on Nov 1, 2017

    @wolfgang42
    Author

    Kevin Donnelly (@kpdonn) The Observable in my example was actually from a library, so I can't change its declaration. Hopefully someone else will find that useful, though.

    [I've since had to rewrite the code that had this issue for other reasons, this is not currently a problem for me any more.]

  7. typescript-bot commented on Nov 21, 2017

    @typescript-bot
    Contributor

    Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

  8. 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

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions