我有一个类型:
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];
}
}
如果foo
,bar
和baz
不是可选的,则其行为与我预期的一样。如何计算可选值?
答案 0 :(得分:1)
有两个问题:
{ [K in keyof T]: T[K] extends TK ? K : never }
类型。这意味着当您对其进行索引时,这些值中会包含未定义的内容。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