roemer
roemer

Reputation: 537

keyof Foo vs Object.keys(Foo)

Can anyone explain why this:

let foo = new Foo(),
    x: (keyof Foo)[] = Object.keys(foo)

leads to:

Type 'string[]' is not assignable to type '"bar" | "bar1"...'

(Using tsc 2.1.4)

Upvotes: 1

Views: 1500

Answers (3)

Nitzan Tomer
Nitzan Tomer

Reputation: 164139

You can type assert it and then it's fine:

class Foo {
    x: number;
    y: number;
}

type Keys = keyof Foo;

let foo = new Foo(),
    x = Object.keys(foo) as Keys[];

(code in playground)

The problem is that you are trying to put string[] into (in my example) ('x' | 'y')[] which is more specific.
The same will happen here:

interface A {
    x: number;
}

interface B extends A {
    y: number;
}

let a: A = { x: 5 };
let b1: B = a; // error
let b2: B = a as B; // fine

When you want to turn something into a more specific type then you need to tell the compiler that you know what you're doing by type asserting it.

Upvotes: 2

Fenton
Fenton

Reputation: 250882

The method Object.keys(...) returns an array of strings:

class Foo {
    public foo: string;
    public bar: string;
}

let foo = new Foo(),
    x: string[] = Object.keys(foo)

TypeScript doesn't fully evaluate what the result of Object.keys will be in order to guarantee the values will be the right ones... although you can see that they will be compatible in this example...

type example = keyof Foo; // "foo", "bar"

So your type annotation is correct in your original example, but TypeScript doesn't give such a specific type to the Object.keys result.

Upvotes: 0

Amid
Amid

Reputation: 22352

I believe this is due to the fact that keyof operator operates in type declaration space whereas Object.keys returns you the variable that is from variable declaration space. And you cant simply assign values from different spaces to each other.

More on the matter: spaces

Upvotes: 2

Related Questions