typescript 如何创建类型安全的泛型函数

332nm8kg  于 2023-02-05  发布在  TypeScript
关注(0)|答案(1)|浏览(248)

我认为这是可能的,但我有麻烦搜索正确的关键字。我有一个方法,用于设置一个对象的多个属性,我希望它类型安全。

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
}

有没有一种方法可以输入payload,以便调用函数将被限制为正确的payload类型?我知道我可以做payload: ICaseDetails | IObservations,但我希望像payload: typeof keyof ICase这样的东西

tcomlyy6

tcomlyy61#

如标题所示,应按如下方式创建函数generic

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

不将propName参数注解为keyof ICase类型(等价于联合类型"caseDetails" | "observations"),我们将其注解为泛型类型K,该泛型类型已被约束为keyof ICase。这允许propNamekeyof ICase * 更具体 *。例如,如果您将"caseDetails"作为propName传入,编译器将推断K是字符串文字类型"caseDetails"
注意,payload的类型是indexed access typeICase[K],意味着ICase的属性在K类型的键上的类型,所以如果K"caseDetails",那么ICase[K]就是ICaseDetails,这(大部分)确保了你为payload传入的值是合适的。
赋值state[propName] = payload类型检查是因为编译器将赋值的两端视为相同的泛型类型ICase[K]
让我们来测试一下:

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

看起来不错。编译器允许有效调用,(大部分)不允许无效调用。
好吧,我一直在说"大部分"。泛型涉及联合时存在类型安全漏洞;如果由于某种奇怪原因,您传入了一个类型为键类型联合的propName参数,那么K将被推断为联合,且ICase[K]也将是联合,这意味着可能允许错误调用:

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

上面没有错误,但是您有99%的可能性将"observations"作为propName传入,将ICaseDetails作为payload传入。
如果这真的很重要,可以开始重写updateCasePart来减少这种情况的发生......但是由于TypeScript不是完全类型安全的,因此您无法真正防止每一个可能的不安全操作,而且付出的努力会导致回报递减。
对于大多数用途来说,像上面这样的泛型函数提供了足够的安全性和可用性,所以我不会在这里进一步离题,详细描述在通往纯粹可靠性的无尽的、越来越危险的道路上可能发生的偏离。
Playground代码链接

相关问题