简体   繁体   English

如何制作类型安全的泛型 function

[英]How can I make a typesafe generic function

I think this is possible but I'm having trouble searching for the right keywords.我认为这是可能的,但我在搜索正确的关键字时遇到了问题。 I have a method that is used to set multiple properties of an object and I'd like it type safe.我有一个用于设置 object 的多个属性的方法,我希望它类型安全。

interface ICase {
  caseDetails: ICaseDetails;
  observations: IObservations;
}
interface ICaseDetails {
  a: string;
}
interface IObservations {
  b: number;
}

const updateCasePart = (state: ICase, payload: any, propName: keyof ICase) => { 
  state[propName] = payload
}

Is there a way I can type payload so that a calling function would be limited to the correct payload types?有没有一种方法可以输入有效负载,以便将调用 function 限制为正确的有效负载类型? I know I could do payload: ICaseDetails | IObservations我知道我可以做payload: ICaseDetails | IObservations payload: ICaseDetails | IObservations but I was hoping for something like payload: typeof keyof ICase payload: ICaseDetails | IObservations但我希望得到类似payload: typeof keyof ICase的东西

As your title suggests, you should make the function generic as follows:正如您的标题所暗示的那样,您应该按如下方式使 function通用

const updateCasePart = <K extends keyof ICase>(
  state: ICase, payload: ICase[K], propName: K
) => { state[propName] = payload }

Instead of annotating the propName parameter as being of type keyof ICase (equivalent to the union type "caseDetails" | "observations" ), we annotate it as being of the generic type K which has been constrained to keyof ICase .我们没有将propName参数注释keyof ICase类型(相当于联合类型"caseDetails" | "observations" ),而是将其注释为通用类型K ,该类型已被限制keyof ICase This allows propName to be more specific than keyof ICase .这允许propNamekeyof ICase更具体 For example, if you pass in "caseDetails" as propName , the compiler will infer that K is the string literal type "caseDetails" .例如,如果您将"caseDetails"作为propName传入,编译器将推断K是字符串文字类型"caseDetails"

And note that the type of payload is the indexed access type ICase[K] , meaning the type of the property of ICase at a key of type K .请注意, payload的类型是索引访问类型ICase[K] ,这意味着ICaseK类型的键上的属性类型。 So if K is "caseDetails" , then ICase[K] is ICaseDetails .所以如果K"caseDetails" ,那么ICase[K]就是ICaseDetails This (mostly) ensures that the value you pass in for payload will be appropriate.这(主要)确保您为payload传递的值是合适的。

The assignment state[propName] = payload type checks because the compiler sees both sides of the assignment as being of the identical generic type ICase[K] .赋值state[propName] = payload type 检查是因为编译器将赋值的两边视为相同的通用类型ICase[K]


Let's test it out:让我们测试一下:

declare const caseDetails: ICaseDetails;
declare const observations: IObservations;
declare const state: ICase;

updateCasePart(state, caseDetails, "caseDetails"); // okay
updateCasePart(state, caseDetails, "observations"); // error!
// -----------------> ~~~~~~~~~~~
// Argument of type 'ICaseDetails' is not assignable to parameter of type 'IObservations'.
updateCasePart(state, observations, "observations"); // okay

Looks good.看起来不错。 The compiler allows valid calls and (mostly) disallows invalid calls.编译器允许有效调用并且(大部分)不允许无效调用。


Okay I keep saying "mostly".好吧,我一直说“大部分”。 There's a type safety hole with generics involving unions;有一个类型安全漏洞 generics 涉及工会; if for some strange reason you pass in a propName argument whose type is a union of key types, then K will be inferred as that union, and ICase[K] will also be a union, meaning it's possible for bad calls to be allowed:如果出于某种奇怪的原因你传入了一个类型为键类型联合的propName参数,那么K将被推断为该联合,并且ICase[K]也将是一个联合,这意味着可能允许错误调用:

updateCasePart(state, caseDetails,
  Math.random() < 0.99 ? "observations" : "caseDetails"
); // okay?!

There's no error in the above, but there's a 99% chance you've passed in "observations" as propName with an ICaseDetails as payload .上面没有错误,但是有 99% 的可能性你已经将"observations"作为propName传递给ICaseDetails作为payload

If it really matters, it's possible to start rewriting updateCasePart to make such things less likely... but since TypeScript isn't fully type safe , you can't really prevent every possible unsafe operation, and there are diminishing returns with added efforts.如果它真的很重要,可以开始重写updateCasePart以减少此类事情发生的可能性......但是由于TypeScript 不是完全类型安全的,你不能真正阻止每一个可能的不安全操作,并且付出更多的努力会减少回报。

For most purposes, a generic function like the above provides adequate safety and usability, and so I won't digress here further by detailing possible excursions down the endless and increasingly treacherous endless road toward pure soundness.对于大多数用途,像上面这样的通用 function 提供了足够的安全性和可用性,因此我不会在这里进一步离题,详细说明在通往纯粹稳健性的无尽且越来越危险的无尽道路上可能发生的偏移。

Playground link to code 游乐场代码链接

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

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