0

I have succeeded at getting my code to compile and run fine, but my Typescript editor is still reporting a warning for val in line 57 at Playground Link

    for (const [key, val] of Object.entries(searchInvocation)) {
      encodedParams.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`);
    }

I cannot fathom how to eliminate the warning. Can anyone see an obvious change which would give the required narrowing?

I thought the key and val destructured from searchInvocation must be a SearchField and string respectively, according to this type specification...

type SearchField = "q" | "sitename" | "filetype";

type SearchInvocation = {
  [K in SearchField]?: string
}

The error is reported as...

const val: string | undefined
Argument of type 'string | undefined' is not assignable to parameter of type 'string | number | boolean'.
  Type 'undefined' is not assignable to type 'string | number | boolean'.ts(2345)

...but I don't know how it could ever be undefined.

Perhaps it's warning me that the Object could conform to the shape of the SearchInvocation type, but other (unmanaged) key value pairs could have been added on top, and their contents are unknown.

However, I don't get an equivalent warning from traversing Object.entries() from the ProxyInvocation type, which looks somewhat equivalent, but must be implicitly guarded.

If unmanaged keys is the source of the issue, I don't know how to add runtime guards which would filter the keys I am traversing to only the keys managed by the SearchInvocation type. I think that would involve reading the union of string literals listed in SearchField at javascript runtime to filter the others out. Or perhaps I can impose a further limitation on the SearchInvocation type to never accept keys which are not from SearchField.

cefn
  • 2,895
  • 19
  • 28
  • https://github.com/microsoft/TypeScript/pull/12253#issuecomment-263132208 explains that Objects could end up with keys which were not managed by the type system. My solution was to add a typeof typeguard https://www.typescriptlang.org/docs/handbook/advanced-types.html#typeof-type-guards – cefn Aug 21 '20 at 01:44
  • There is a related and more detailed discussion of the issue in the context of Object.keys but which shows the same issue... https://stackoverflow.com/questions/55012174/why-doesnt-object-keys-return-a-keyof-type-in-typescript – cefn Aug 21 '20 at 01:48

1 Answers1

1

your error is because the properties in the type SearchInvocation are optional, and for this reason, the val in the for of could be undefined.

You can use the Non-null assertion operator (I don't like to use it because for me is like use any) to say to Typescript "I know that this value never will be undefined, don't check that here"

About this operator https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator

Oscar Velandia
  • 1,157
  • 1
  • 7
  • 9
  • But if one was left out (as optional) then the entry wouldn't be there, so it wouldn't be traversed by Object.entries() and therefore generate an undefined value. – cefn Aug 21 '20 at 01:36
  • For this reason I think it's more to do with the Typescript design decision described at https://github.com/microsoft/TypeScript/pull/12253#issuecomment-263132208 and the bleeding in of keys and values entirely outside the type. – cefn Aug 21 '20 at 01:38
  • 1
    Probably is for that reason, and in this case, use Non-null assertion is secure – Oscar Velandia Aug 21 '20 at 01:45
  • Yes, in the end I coded defensively using `typeof` for the possibility that SearchInvocation was ever extended (e.g. there was a future object which subclassed SearchInvocation but had extra non-string-valued properties which might resolve to undefined). As long as I promise to myself never to subclass it, the Non-null assertion is secure... :) – cefn Aug 21 '20 at 01:51