简体   繁体   English

强制参数成为扩展(或部分)类型的其他属性的键

[英]Enforce argument to be a keyof other property that extends (or partially) a type

I have a function called updateMany which accepts a generic type of an entity and allows to update certain data based on the entity and a "by" property which should be a keyof data .我有一个名为updateMany的 function ,它接受实体的通用类型,并允许根据实体和“by”属性更新某些数据,该属性应该是 keyof data Something like this:像这样的东西:

function updateMany<Table>() {
  return <
    Data extends { [column in keyof Table]?: Table[column] },
    By extends keyof Data
  >(params: {
    by: By;
    data: Table[];
  }) => {};
}

(I wrapped the function inside another function since I wasn't sure how to achieve it in a single function) (我将 function 包裹在另一个 function 中,因为我不确定如何在单个函数中实现它)

Now, everything seems to be working, except the fact that data has to have all of the properties.现在,一切似乎都在工作,除了data必须具有所有属性这一事实。 When I try to wrap it inside Partial , then I can pass to by properties that doesn't exists in data .当我尝试将它包装在Partial中时,我可以通过data中不存在by属性传递给它。 I know that my implementation is incorrect, but this is as far as I could get it to what I want.我知道我的实现是不正确的,但这是我能做到的。

Play in playground 在操场上玩耍

My suggestion here would be to write updateMany() like this:我的建议是这样写updateMany()

function updateMany<T>() {
  return <
    K extends keyof T,
    P extends K
  >(params: {
    by: P;
    data: (Pick<T, K> & Partial<T>)[];
  }) => { };
}

Both my version and yours use currying to work around the lack of "partial type parameter inference" (as requested in microsoft/TypeScript#26242 ), since there's no way to call a function with multiple type parameters like T , K , and P and have the caller manually specify T while letting the compiler infer K and P .我和你的版本都使用柯里化来解决缺少“部分类型参数推断”的问题(如microsoft/TypeScript#26242中所要求的),因为无法调用具有多个类型参数(如TKP )的 function 和让调用者手动指定T ,同时让编译器推断KP Currently in TypeScript it's all or nothing;目前在 TypeScript 中,要么全有,要么全无; you can either manually specify all three, or let the compiler infer all three.您可以手动指定所有三个,也可以让编译器推断所有三个。 Currying sidesteps this by having one function where you specify T return another function where the compiler infers K and P . Currying 通过让一个 function 您指定T返回另一个 function 来回避这一点,其中编译器推断KP

Anyway, K corresponds to the keys present on the elements of data , and P corresponds to the key specified in by .无论如何, K对应于data元素上的键, P对应于 中指定by键。 I do this with two type parameters instead of just K for both, since we don't want the compiler to infer that K is the union of the key from by and the ones in data .我使用两个类型参数而不是只使用K来执行此操作,因为我们希望编译器推断Kby中的键和data中的键的并 Instead we constrain P extends K so that whatever K is inferred from data , we require the key in by to be in that set.相反,我们约束P extends K ,以便从data推断出的任何K ,我们都要求 key in by在该集合中。

Conceptually we would have data be of type Pick<T, K>[] and that would be it.从概念上讲,我们将拥有Pick<T, K>[]类型的data ,就是这样。 But since you said you wanted some better IntelliSense autocompletion for data , I've intersected Pick<T, K> with Partial<T> .但既然你说你想要一些更好的IntelliSense 自动完成data ,我已经将Pick<T, K>Partial<T> 相交 Luckily the compiler seems to use Pick<T, K> to infer K to be only those keys actually present in data entries, while also using Partial<T> to provide hints for autocompletion.幸运的是,编译器似乎使用Pick<T, K>来推断K只是data条目中实际存在的那些键,同时还使用Partial<T>来提供自动完成的提示。

Let's see if it works:让我们看看它是否有效:

const updateManyEntities = updateMany<Entity>();;

const X1 = updateManyEntities({ by: "A", data: [{ A: 1 }] }); // okay
const X2 = updateManyEntities({ by: "A", data: [{ A: 1, B: 2, C: 2 }] }); // okay
const X4 = updateManyEntities({ by: "C", data: [{ A: 1, B: 2 }] }); // error!
// ---------------------------> ~~
// Type '"C"' is not assignable to type '"A" | "B"'
const X3 = updateManyEntities({ by: "D", data: [{ A: 1, B: 2, C: 2 }] }); // error!
// ---------------------------> ~~
// Type '"D"' is not assignable to type '"A" | "B" | "C"'
const X5 = updateManyEntities({ by: "D", data: [{ A: 1, B: 2, C: 3, D: 4 }] }); // error!
// ---------------------------> ~~                            ----> ~~~~
// Type '"D"' is not assignable to type 'keyof Entity'        |
// Object literal may only specify known properties, and 'D' does not exist in type...

Looks good!看起来不错!

Playground link to code Playground 代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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