实施路径类型而不考虑可选属性

时间:2018-03-19 15:04:14

标签: typescript

我正在编写一个辅助函数,它在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');

只要authorname属性都不是可选的,上述工作就很好,在这种情况下编译器会抱怨

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'.

1 个答案:

答案 0 :(得分:1)

问题是,对于接口的可选字段,字段的类型将为TField|undefined。对于联合类型,我们只能访问公共字段,因此这意味着TField中没有任何字段会显示在keyof TField|undefined中,因为TFieldundefined没有任何共同之处{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