Skip to content
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

Typescript should be able to derive the type of the array created by Object.keys from the key signature of the object #28284

Closed
quinn opened this issue Nov 1, 2018 · 7 comments
Labels
Duplicate An existing issue was already created

Comments

@quinn
Copy link

quinn commented Nov 1, 2018

TypeScript Version: 3.2.0-dev.201xxxxx

Search Terms:
object.keys
Code

type Keys = 'one' | 'two'

const obj:{[key in Keys]: string} = {
  one: 'value for one',
  two: 'value for two',
}

Object.keys(obj).forEach(key =>
  console.log(obj[key])
)

Expected behavior:

No error.

Actual behavior:

$ tsc --noImplicitAny test.ts
test.ts:9:15 - error TS7017: Element implicitly has an 'any' type because type '{ one: string; two: string; }' has no index signature.

9   console.log(obj[key])
                ~~~~~~~~

Playground Link:

Related Issues:

@ahejlsberg
Copy link
Member

Duplicate of #26901 (any many others).

@ahejlsberg ahejlsberg added the Duplicate An existing issue was already created label Nov 1, 2018
@quinn
Copy link
Author

quinn commented Nov 1, 2018

@ahejlsberg thanks for the additional context. If this isn't possible, could someone at least explain or link to what is the most idiomatic typescript in these situations?

@ahejlsberg
Copy link
Member

@quinn If you are certain obj has no other enumerable properties you can assert that using a type assertion. Either of the following:

type Keys = 'one' | 'two';

const obj:{[key in Keys]: string} = {
  one: 'value for one',
  two: 'value for two',
};

Object.keys(obj).forEach(key => console.log(obj[key as Keys]));

(Object.keys(obj) as Keys[]).forEach(key => console.log(obj[key]));

@quinn
Copy link
Author

quinn commented Nov 1, 2018

@ahejlsberg using as fixed it, thank you! My assumption would be that if I somehow mutated the object to have a key other than 'one' or 'two' the fact that i have a defined type for the key [key in Keys] typescript would catch that.

@quinn
Copy link
Author

quinn commented Nov 1, 2018

and I definitely feel like i'm not doing something right if i ever need to use as. I really hate to rely on being certain myself rather than letting typescript to its thing.

@fenying
Copy link

fenying commented Nov 30, 2018

Emmm, same problem with for-in:

let a: ClassA;

for (let k in a) {

    console.log(a[k]); // [ts] Element implicitly has an 'any' type because type 'ClassA' has no index signature. [7017]
}

And must be written in this style:

let a: ClassA;
let k: keyof ClassA;
for (k in a) {

    console.log(a[k]); // That's okay.
}

So, why doesn't TS give k the type keyof ClassA automatically?

@RyanCavanaugh
Copy link
Member

RyanCavanaugh commented Nov 30, 2018

So let's say you write

const obj = { x: 1, y: 2 };

keyof typeof obj has type "x" | "y"

It would be bad if TypeScript allowed a value "z" to appear in an expression typed as "x" | "y". But that's exactly what's at stake here:

type Point = { x: number, y: number };

function fn(x: Point) {
  // Proposal: make this *not* an error
  for (let k in x) printXorY(k);
}

function printXorY(key: keyof Point) {
  switch(key) {
    case "x":
      console.log("is x");
      break;
    case "y":
      console.log("is y");
      break;
    default:
      throw new Error("HOW IS THIS HAPPENING");
  }
}

const p3 = { x: 1, y: 2, z: 3 };
fn(p3); // oops

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants