从属性为可选的类型中获取类型为K的属性

时间:2019-10-27 05:39:40

标签: typescript

我有一个类型:

interface IThing {
   foo?: number[]
   bar?: number[]
   baz?: number[]
}

我想编写一个通用方法来删除类型为number[]的空数组的任何属性的值。

我以此为起点:

type FilteredKeyOf<T, TK> = keyof Pick<T, { [K in keyof T]: T[K] extends TK ? K : never }[keyof T]>

function deleteIfEmpty(value: IThing, key: FilteredKeyOf<IThing, number[]>) {
  const v = value[key]
  if (v && v.length === 0) {
      delete v[key];
  }
}

如果foobarbaz不是可选的,则其行为与我预期的一样。如何计算可选值?

1 个答案:

答案 0 :(得分:1)

有两个问题:

  1. 可选属性在中间是可选的 { [K in keyof T]: T[K] extends TK ? K : never }类型。这意味着当您对其进行索引时,这些值中会包含未定义的内容。
  2. T[K]将包括未定义的可选值,因此它们将不会扩展TK,仅扩展TK |。未定义。

您可以做的是在映射部分中完成所有必需的操作,并在过滤时允许未定义:

type FilteredKeyOf<T, TK> = keyof Pick<T, 
    { [K in keyof Required<T>]: T[K] extends TK | undefined ? K : never }[keyof T]>

完整示例(playground):

interface IThing {
    otherVal: string;
    undef: undefined;
    optString: string | undefined;
    foo?: number[];
    bar: number[] | undefined;
    baz: number[];
}

type FilteredKeyOf<T, TK> = keyof Pick<T, 
    { [K in keyof Required<T>]: T[K] extends TK | undefined ? K : never }[keyof T]>

function deleteIfEmpty(value: IThing, key: FilteredKeyOf<IThing, number[]>) {
  const v = value[key]
  if (v && v.length === 0) {
      delete value[key];
  }
}

declare const thing: IThing;

deleteIfEmpty(thing, "foo"); // Optional OK
deleteIfEmpty(thing, "bar"); // May be undefined OK
deleteIfEmpty(thing, "baz"); // Required OK
deleteIfEmpty(thing, "undef"); // Edge case I think
deleteIfEmpty(thing, "optString"); // Error
deleteIfEmpty(thing, "otherVal"); // Error
deleteIfEmpty(thing, "nonExistent"); // Error