10
votes

Typescript 1.8 introduced the string literal type. However, when passing in an object as a parameter as in the following:

const test = {
    a: "hi",
    b: "hi",
    c: "hi"
};

interface ITest {
    a: "hi" | "bye"
}

function testFunc (t: ITest) {

}

testFunc(test);

It fails with:

Argument of type '{ a: string; b: string; c: string; }' is not assignable to parameter of type 'ITest'. Types of property 'a' are incompatible. Type 'string' is not assignable to type '"hi" | "bye"'. Type 'string' is not assignable to type '"bye"'.

I would expect this to work since it meets the requirements of the interface, but I might be overlooking something.

1
a, b, and c are string type in test and the "hi" | "bye" type in your interface.nullforce
That makes sense, even though it's a bit counter intuitive. Thanks.Alexander Mattoni

1 Answers

13
votes

The type of test.a has been inferred as string and not "hi". The compiler is comparing the types and not the initial string expression.

In order to make this work you need to type that property as "hi" | "bye":

type HiBye = "hi" | "bye";

const test = {
    a: "hi" as HiBye,
    b: "hi",
    c: "hi"
};

interface ITest {
    a: HiBye
}

function testFunc (t: ITest) {
}

testFunc(test);

Note that in the original case it wouldn't make sense for the compiler to infer the type of test.a to be "hi" because you can assign a different value to test.a before it reaches testFunc(test)—ex. test.a = "not hi".

Side note: It's good the compiler doesn't infer the type the be the string expression for even constant string variables. That would also lead to a lot of annoyances... imagine this:

const myVariableTypedAsHi = "hi";   // implicitly typed as "hi"
let otherVar = myVariableTypedAsHi; // otherVar implicitly typed as "hi"

otherVar = "test"; // error: cannot assign `"test"` to `"hi"`—well that would be annoying