【问题标题】:How to scope TypeScript generic type with nested interface如何使用嵌套接口限定 TypeScript 泛型类型
【发布时间】:2020-01-01 01:12:45
【问题描述】:

我正在尝试为接受三个参数的函数创建一个通用接口。填写第一个参数时,第二个的选项应该在第一个的范围内。第三个参数应该在第二个参数的范围内。

我当前的代码如下所示:

type SettingActionCreator<T> = <
  L extends T,
  K extends keyof L,
  C extends keyof L[K]
>(
  type: K,
  settingKey: keyof L[K],
  settingValue: L[K][C]
)

export interface SomeSettings {
  [Type.TypeA]: {
    settingA: "value1" | "value2";
    settingB: "value3 | "value4";
  };
  [Type.TypeB]: {
    ...
  };
  [Type.TypeC]: {
    ...
  };
}

const setSomeSetting: SettingActionCreator<SomeSettings> = (
  type,
  settingKey,
  settingValue
) =>
  // do something

所以当我想用参数调用setSomeSetting 时,我希望我的范围对于我传入的每个参数都会变小。

例如,如果我填写第一个参数,那么 SettingActionCreator 会确保第二个参数 (settingKey) 只能是 settingA 或 settingB。到目前为止,一切都按预期工作。

setSomeSetting(Type.TypeA, ....) // <-- settingKey accepts "settingA" and "settingB"

但是对于第三个论点,我似乎无法解决它。当我设置第一个参数和第二个参数时,我希望第三个参数只接受所选 settingKey 参数中的联合选项。但是,我在Type.TypeA 中获得了所有选项。所以settingA和settingB的settingValues都是

使用此代码:

setSomeSetting(Type.TypeA, "settingA", "value3") // <-- settingValue should only accept "value1" or "value2"

我预计会出现如下错误:

TS2345: Argument of type '"value3"' is not assignable to parameter of type `"value1" | "value2"

但我没有收到任何类型错误,因为它同时接受 settingA 和 settingB 的值。

有人对我做错了什么有建议吗?

【问题讨论】:

  • 你能创建一个带有问题工作示例的打字稿游乐场链接吗? typescriptlang.org/play 通过工作演示回答这样的问题要容易得多。
  • 我不确定你想要的是否完全可能。它接受settingA 和settingB 这两个值的原因是因为它们都是适合您所描述的类型系统的值。没有办法进一步缩小范围,因为这需要在 typescript 解析时不存在的运行时信息。我建议您重新设计您的设计,以减少打字系统的嵌套,或者考虑为最终选项抛出运行时错误
  • Idk,我怀疑一定有办法让这种事情发挥作用。如果不使用普通参数,那么它必须使用柯里化函数,对吧?

标签: typescript typescript-generics


【解决方案1】:

您需要向 TypeScript 证明 settingKey 与 settingValue 相关 - 您声明中的 settingKey: keyof L[K] 令人困惑(我不知道为什么 - 它感觉 type C extends keyof L[K] != keyof L[K] 的错误,但它绝对是类型推断代码的限制)。

将该参数的类型更改为settingKey: C,TypeScript 将能够理解三个参数之间的关系:

type SettingActionCreator<T> = <
  L extends T,
  K extends keyof L,
  C extends keyof L[K]
>(
  type: K,
  settingKey: C, // <-- changed from keyof L[K]
  settingValue: L[K][C]
) => void;


declare const Type: {
  readonly TypeA: unique symbol,
  readonly TypeB: unique symbol,
  readonly TypeC: unique symbol
}

export interface SomeSettings {
  [Type.TypeA]: {
    settingA1: "valueA1" | "valueA2";
    settingA2: "valueA3" | "valueA4";
  },
  [Type.TypeB]: {
    settingB1: "valueB1" | "valueB2",
    settingB2: "valueB3" | "valueB4"
  },
  [Type.TypeC]: {
    //
  }
}

const setSomeSetting: SettingActionCreator<SomeSettings> = (
  type,
  settingKey,
  settingValue
) => {}

这会导致您正在寻找的错误类型:

setSomeSetting(Type.TypeB, "settingB1", "valueA3")
// Argument of type '"valueA3"' is not assignable to parameter of type '"valueB1" | "valueB2"'.

【讨论】:

  • 非常感谢,这按预期工作。我仍然很困惑C 与keyof L[K] 有何不同。
猜你喜欢
  • 2019-05-06
  • 1970-01-01
  • 2018-07-23
  • 2019-07-21
  • 2022-01-20
  • 2021-10-29
  • 1970-01-01
  • 2020-01-10
  • 1970-01-01
相关资源
最近更新 更多