8

I have an object type:

interface ShortUrlParam {
    openid: string;
    avatar: string;
    nickname: string;
}

const param: ShortUrlParam = {
    openid: 'abc123',
    avatar: '',
    nickname: 'wenzi'
}

let query = '';
for(let key in param) {
    query += `&${key}=${encodeURIComponent(param[key])}`; // error tip
}

in param[key], error tip is:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'ShortUrlParam'. No index signature with a parameter of type 'string' was found on type 'ShortUrlParam'.ts(7053)

I have two programmes, but not perfect.

1. redefine interface ShortUrlParam

interface ShortUrlParam {
    openid: string;
    avatar: string;
    nickname: string;
    [key: string]: string;
}

2. param as any

for(let key in param) {
    query += `&${key}=${encodeURIComponent((param as any)[key])}`;
}

my question is there has any better solution?

3 Answers 3

17

For better or worse, TypeScript does not let you give a type annotation to a variable introduced in a for..in loop (see microsoft/TypeScript#3500). This is justified by reasons of soundness; in TypeScript, object types are open and not exact (see microsoft/TypeScript#12936), so it's always possible for an object to have unknown extra properties you weren't expecting. All we really know about key in for (let key in param) {} is that key is a string: we can't safely narrow it to keyof ShortUrlParam.

Except of course we can because you created it yourself as an object literal several lines earlier and we know we didn't add extra properties. One suggested workaround if you want to use a type annotation is to declare the variable before the loop and give it the type you want:

let key: keyof ShortUrlParam; // this is allowed
for (key in param) {
  query += `&${key}=${encodeURIComponent(param[key])}`;  // no error
}

This works with no error (but beware if it turns out someone has added an unexpected property to param when you weren't looking).


A similar approach is to leave the loop declaration alone but to assert that key is of type keyof ShortUrlParam:

for (let key in param) {
  query += `&${key}=${encodeURIComponent(param[key as keyof ShortUrlParam])}`; // okay
}

I'd say both of those are somewhat safer than using any, since at least you'd catch errors if someone added a property to ShortUrlParam that isn't accepted by encodeURIComponent().

Anyway, hope that helps; good luck!

Playground link to code

Sign up to request clarification or add additional context in comments.

Comments

0

Slightly less safe than jcalz's answer, but useful for type hints in case you don't have a specific type definition handy (because you didn't declare an object literal one line before). You can infer it instead of falling back to any. Just don't fall back to any...

Also the js heap in the scope of a for loop is destroyed every iteration, so we can use const instead of let to assert that key won't change. If you dó change the key you loop over, just use a regular old for-loop to avoid messy code.

Generic example:

for (const key in thing) {
  doThings(key as keyof typeof thing)
}

Example with the context of your question:

for (const key in param) {
  query += `&${key}=${encodeURIComponent(param[key as keyof typeof param])}`
}

Comments

-2

Try:

for(var i=0;i<param.length; i++) {
  let key  =   param[i];
  query += `&${key}=${encodeURIComponent((param as any)[key])}`;
}

2 Comments

param is an object type, this solution can work? and, this is like my second programme, I want to del any
This answer does not address the question. param has no length property, nor does it have a numeric index signature.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.