简体   繁体   中英

How to Type a deep object destructuring in Typescript?

I'm trying to Type a deep object destructuring in Typescript and I have searched a whole bunch but found nothing that quite answers my question, so I thought I'd ask it.

I have something like this in my class:

public Example(
  key1: string,
  key2: string,
  val: string,
  callback: (list: string[]) => void
): void
{
  chrome.storage.sync.get(
    ({
      [key1]: list1,
      [key2]: list2
    }) => {
      list1.includes(val)
        ? callback(list1.filter(i => i !== val))
        : callback(list2)
    }
  );
}

I haven't found a way to Type list1 and list2 (as string[] ) and they get set as any which is no use to me when trying to use array functions. I've found examples of how to Type if the key is known ( { key }: { key: string[] } ) but not for my situation. I also can't create a Type as the LHS is non-static.

My example is altered somewhat but contains everything needed to get my question across.

In case it's needed, the chrome.d.ts I have states:

/**
 * Gets one or more items from storage.
 * @param callback Callback with storage items, or on failure (in which case runtime.lastError will be set).
 * Parameter items: Object with items in their key-value mappings.
 */
get(callback: (items: { [key: string]: any }) => void): void;

I'm sure it's probably really simple and I've just not found anything; that being said, this question might make it easier for other people to find in future so it'd be win-win if solved.

I appreciate all and any help.


EDIT: I guess I'm also looking for the case where it's not just string[] that is returned. What if you have a whole bunch of data that were all different types?

So you are getting type any because the get function has it like that.

Hence you could do this:

type MyGet = (callback: (items: {[key: string]: string[]}) => void) => void

// inside the Example method
(chrome.storage.sync.get as MyGet)(
          ({
            [key1]: list1,
            [key2]: list2
          }) => {
            list1.includes(val)
              ? callback(list1.filter(i => i != val))
              : callback(list2)
          }
        );

The other and the better approach in case if there is a possibility to encounter other types, is to use type guards:

chrome.storage.sync.get(
          ({
            [key1]: list1,
            [key2]: list2
          }) => {

            if(Array.isArray(list1)) {
              list1.includes(val)
                ? callback(list1.filter(i => i != val))
                : callback(list2)
            }
          }
        );

This is OK in terms of type safety, because narrowing down type any to string[] should be considered OK.

Use declaration merging to correct the third-party types:

chrome.d.ts

import {} from 'chrome';

declare namespace chrome.storage {
  export interface StorageArea {
    get(callback: (items: { [key: string]: string[] }) => void): void;
  }
}

In case you were wondering, the empty import statement on the top of the file is required for the file to be treated as module augmentation instead of its definition.

The best thing about it? If you're not happy with string[] there, you can augment it in some other way. You can also make get generic.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM