Skip to content

String literal types in complex type expressions #7199

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

Closed
scharf opened this issue Feb 23, 2016 · 7 comments
Closed

String literal types in complex type expressions #7199

scharf opened this issue Feb 23, 2016 · 7 comments
Assignees
Labels
Bug A bug in TypeScript Domain: Error Messages The issue relates to error messaging Fixed A PR has been merged for this issue

Comments

@scharf
Copy link

scharf commented Feb 23, 2016

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;
}
//...
@scharf
Copy link
Author

scharf commented Feb 23, 2016

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

@jeffreymorlan
Copy link
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>({ ... })

@weswigham
Copy link
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.

@yuit
Copy link
Contributor

yuit commented Feb 23, 2016

It is as @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'.

@sandersn
Copy link
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.

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

@sandersn sandersn added Bug A bug in TypeScript Domain: Error Messages The issue relates to error messaging labels Feb 23, 2016
@scharf
Copy link
Author

scharf commented Feb 23, 2016

@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;
});

@mhegazy
Copy link
Contributor

mhegazy commented Feb 23, 2016

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

var foobar: FooBar = mix<Foo>({
    //...
    nowOk: "k";
});

@mhegazy mhegazy added Suggestion An idea for TypeScript In Discussion Not yet reached consensus and removed Bug A bug in TypeScript labels Feb 23, 2016
@mhegazy mhegazy removed the Domain: Error Messages The issue relates to error messaging label Feb 23, 2016
@DanielRosenwasser DanielRosenwasser added Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Domain: Error Messages The issue relates to error messaging Bug A bug in TypeScript and removed Suggestion An idea for TypeScript In Discussion Not yet reached consensus Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. labels Feb 23, 2016
@DanielRosenwasser DanielRosenwasser added this to the TypeScript 2.0 milestone Feb 23, 2016
@DanielRosenwasser DanielRosenwasser added the Fixed A PR has been merged for this issue label Feb 25, 2016
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug A bug in TypeScript Domain: Error Messages The issue relates to error messaging Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

7 participants