Skip to content

String literal types in complex type expressions #7199

Description

TypeScript Version:

1.8.2

Code

interface Foo {
    ok:string;
    okToo: number;
    notOk:  "o" | "k";
}

interface Bar {
    bar: string;
}

interface FooBar extends Foo, Bar{
}

function mix<T>(obj:T): T & Bar {
    (obj as any).bar="bar";
    return obj as T & Bar;
}

var fooBar:FooBar = mix({
    ok:"ok",
    okToo: 42,
    notOk:"k"
});

Expected behavior:

The assignment to fooBar should be allowed.

Actual behavior:

I get the totally confusing error:

Error:(20, 5) TS2322: Type '{ ok: string; notOk: string; } & Bar' is not assignable to type 'FooBar'.
 Type 'Bar' is not assignable to type 'FooBar'.
 Property 'ok' is missing in type 'Bar'.

The confusing part is that it complains about the first property (ok) and not about notOk.

Looking at the error message it seems that the string literal type notOk:"o"|"k" was converted to notOk :string.

Therefore when I turn the string literal type to a string (notOk:string) everything is OK....

Hint: defining the string literal type as a type does not solve the problem:

type NotOK =  "o" | "k";

interface Foo {
    //...
    notOk: NotOK;
}
//...

Activity

  1. scharf commented on Feb 23, 2016

    @scharf
    Author

    I could do

    var fooBar:FooBar = mix({
        ok:"ok",
        okToo: 42,
        notOk:"k"
    }) as FooBar ;

    but then I loose all the benefits of type checking...

  2. jeffreymorlan commented on Feb 23, 2016

    @jeffreymorlan
    Contributor

    I think you're expecting too much from type argument inference. Give mix an explicit type argument and it should work:
    var fooBar: FooBar = mix<Foo>({ ... })

  3. weswigham commented on Feb 23, 2016

    @weswigham
    Member
    var foo: Foo = {
        ok:"ok",
        okToo: 42,
        notOk:"k"
    };
    var fooBar = mix(foo);

    Should work fine. TS only infers literal types when there is a type annotation implying them.

  4. yuit commented on Feb 23, 2016

    @yuit
    Contributor

    It is as Wesley Wigham (@weswigham) explained. Though we may be should consider give a better error message especially this part of the error is a bit misleading

     Type 'Bar' is not assignable to type 'FooBar'.
     Property 'ok' is missing in type 'Bar'.
    
  5. sandersn commented on Feb 23, 2016

    @sandersn
    Member

    The problem is that, in the object literal passed to mix, notOk is inferred to be string absent any other information. As soon as the compiler knows that notOk: "k" is actually of type "k" or "o" | "k" then the assignment works. The minimal annotation is actually:

    var foobar: FooBar = mix({
      ok:"ok",
      okToo: 42,
      notOk: "k" as "k" // or as "o" | "k"
    });

    The error message you get without the annotation looks completely wrong to me, or at least misleading.

    Daniel Rosenwasser (@DanielRosenwasser) has a PR #6554 that defaults string literals to string literal types, and then widens to string as needed. After that goes in, the type of "k" will be "k" by default.

  6. scharf commented on Feb 23, 2016

    @scharf
    Author

    Nathan Shively-Sanders (@sandersn) that solves the problem :-)

    So, if I define type OK = "o" | "k"; I can use it in the cast:

    type OK =  "o" | "k";
    
    interface Foo {
        //...
        nowOk: OK;
    }
    var foobar: FooBar = mix({
        //...
        nowOk: "k" as OK;
    });
  7. mhegazy commented on Feb 23, 2016

    @mhegazy
    Contributor

    that should work, or even specifying the type argument explicitlly:

    var foobar: FooBar = mix<Foo>({
        //...
        nowOk: "k";
    });
  8. added
    Needs ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.
    BugA bug in TypeScript
    and removed
    SuggestionAn idea for TypeScript
    Needs ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.
    on Feb 23, 2016
  9. locked and limited conversation to collaborators on Jun 19, 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 TypeScriptDomain: Error MessagesThe issue relates to error messagingFixedA PR has been merged for this issue

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions