1

NOT LOOKING FOR A SOLUTION HERE, JUST NEED TO KNOW IF THIS IS A BUG IN TYPESCRIPT. THAT IS ALL.

Is this a bug in TypeScript? Should I report this bug? To my understanding this should work, unless I am doing something wrong here,

interface Something {
    key1: string;
    key2: number;
    key3: boolean;
}

const someObject: Something = {
    key1: '123',
    key2: 123,
    key3: false
}

for (const key in someObject) {
    console.log(someObject[key]); // TS error
}

Note: I am not looking for a workaround, because this works for me. Just looking to know if I am doing something wrong or there is actually a bug in TS.

for (const [key, value] of Object.entries(someObject)) {
    console.log(key, value);
}

or

for (const key in values) {
    console.log(key, values[key as keyof DirectDepositDTO]);
}

I do understand that the type of key is string, but shouldn't it be keyof Something?

TypeScript Playground Link

P.S. The question is marked as a duplicate, but I am not asking for a solution here, I already know how to fix it. All I need to know if that is a bug in TypeScript. Because for (const key in someObject) { means that the key will be keyof Something. I should not need to know re-define the types.

Hafiz Temuri
  • 3,882
  • 6
  • 41
  • 66

1 Answers1

1

You have several ways.

If you want to use for .. in loop, you should allow TS to use indexed properties:

interface Something {
    key1: string;
    key2: number;
    key3: boolean;
}

type Values<T> = T[keyof T]
type MakeIndexed<T> = T & { [prop: string]: Values<T> }
type SomethingIndexed = MakeIndexed<Something>

const someObject: SomethingIndexed = {
    key1: '123',
    key2: 123,
    key3: false
}

for (const key in someObject) {
    const x = someObject[key] // Values<Something>
    const isString = someObject.key1 // string

    console.log(someObject[key]);
}

You can also use more functional approach without extra utility types but with type casting:

    const someObject: Something = {
        key1: '123',
        key2: 123,
        key3: false
    }

    // FP way with more explicit types
    const keys2 = (Object.keys(someObject) as Array<keyof typeof someObject>).forEach(key => {
        const result = someObject[key]; // string | number | boolean
    })
  • Sorry, I am just looking to know if that is a bug in TypeScript or not. Because if I say `for (const key in someObject) {` that is very clear that the type of `key` will be `keyof Something` – Hafiz Temuri Apr 12 '21 at 19:14
  • @Hafiz it is not a bug. It is intentional because you can fulfill an interface/type with an object that has other properties. The interface is just the minimum that you need. So you cannot know that all keys of the object are keys of the interface. – Linda Paiste Apr 12 '21 at 20:19
  • @LindaPaiste Yes, I know you can fill with other things, if that is the case then there should a `strict` option to distinguish whether the object is modified or not. But either the type should be `keyof (typeof values)`. Even if the object is modified, the keys will always come from the `values` object – Hafiz Temuri Apr 12 '21 at 22:42
  • *"the type should be `keyof (typeof values)`. Even if the object is modified, the keys will always come from the `values` object".* That's only true is `typeof values` is exhaustive [which is not always the case](https://tsplay.dev/WvpQrN). – Linda Paiste Apr 12 '21 at 22:55
  • @LindaPaiste I see what you mean actually. I think then we need a specific `strict` type where you cannot destructure the interface like that. If you can write that explanation as an answer, I will mark it as resolved. Because that is what I was looking for. – Hafiz Temuri Apr 13 '21 at 15:55
  • In this case, key does not mean keyof object. It is made by design. Same behavior when you do Object.keys(obj). U will get string[] instead of keyof typeof obj[], because js objects are mutable by nature and TS cant provide any gyaranter that object contains all keys from the interface. What if smbd call delete operator before for..in loop? – captain-yossarian from Ukraine Apr 13 '21 at 17:25