Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript string literal with duck-typed object

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.

like image 222
Alexander Mattoni Avatar asked Apr 05 '16 01:04

Alexander Mattoni


1 Answers

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
like image 160
David Sherret Avatar answered Nov 15 '22 20:11

David Sherret