我正在编写一个辅助函数,它在path
中设置对象的值,如此
function set<
C extends Collection<any>,
E = C extends Collection<infer U> ? U : never,
K1 extends keyof E = keyof E,
K2 extends keyof E[K1]= keyof E[K1]
V extends E[K1][K2]= E[K1][K2]
>(
collection: C,
entityId: string,
path: [K1, K2],
value: V
)
这允许我执行以下操作
Collection.set(myCommentCollection, someComment.id, ['author', 'name'], 'jack');
只要author
或name
属性都不是可选的,上述工作就很好,在这种情况下编译器会抱怨
Argument of type '["author", string]' is not assignable to parameter of type '["author", never]'.
我想要实现的是编译器只强制路径有效。即。路径中的每个属性都已正确定义,无论它们是否可选。
作为函数实现的一部分,如果未定义完整路径,我将提前返回。
这可能吗?感谢
更新
如果stats
是必需的且total
属性是可选的,则以下内容将失败
Collection.set(myCollection, someItem.id, ['stats', 'total'], 10)
错误:
Argument of type '[number, "total"]' is not assignable to parameter of type '[never, "total"]'.
Type 'number' is not assignable to type 'never'.
答案 0 :(得分:1)
问题是,对于接口的可选字段,字段的类型将为TField|undefined
。对于联合类型,我们只能访问公共字段,因此这意味着TField
中没有任何字段会显示在keyof TField|undefined
中,因为TField
与undefined
没有任何共同之处{1}}。
我们可以使用预定义的条件类型从字段类型中删除undefined
:
function set<
C extends Collection<any>,
E = C extends Collection<infer U> ? U : never,
K1 extends keyof E = keyof E,
V1 = Exclude<E[K1], undefined>,
K2 extends keyof V1= keyof V1,
V2 extends V1[K2] =V1[K2]
>(
collection: C,
entityId: string,
path: [K1, K2],
value: V2
) {}
//Usage
interface Comment extends Entity {
author?: {
name?: string
}
stats : {
total?: number
}
}
export interface Collection<T extends Entity> extends Object {
readonly entities: { [key: string]: T };
readonly ids: EntityId[];
}
set(commentState, comment.id, ['author', 'name'], 'jack'); //OK
set(commentState, comment.id, ['stats', 'total'], 10); //OK