我正在试图解决一个问题,迫使框架中的某一"模式",因为缺乏一个更好的词,我正在做的.所讨论的框架是Redux store Composer(给予或接受)的一种形式.
TS操场链接:
- full complex,所有类型都包括在内(这是我需要的,但它很棒)
- Siplified(最小可重现示例-显示相同问题)
要点如下:
我有一个函数,它被用来为名为"createBundle"的框架创建一个"包"或一个插件.这个函数的类型是BundleInitializer
,这是一个复杂的类型,但可以说这个函数的返回类型是Bundle
类型.
其目标是允许开发人员调用"createBundle"函数,然后该函数将获得智能感知支持,并允许更容易地遵循该函数对其接收的参数所需的模式.
BundleInitializer
签名的语义如下(为清楚起见进行了简化):
type BundleInitializer = <
Name extends string,
Selectors extends Record<string, Selector>, //
>(
content: {
name: Name
selectors: Selectors
}
) => Bundle<Name, Selectors>
其中 Select 器的类型是(同样为了简单起见)Selector = () => unknown
.这将允许我们传递一个类似以下形式的参数:
const arg = {
name: "Example",
selectors: {
foo: () => {},
bar: () => {}
}
}
但我的目标是强制 Select 器的命名遵循select${Capitalize<string>}
的模式.因此,我try 使用以下类型来实现这一点(后面的类型都在TS操场链接的"简化"版本中):
type Selector = <S>(state: S) => unknown
interface Bundle<
Name extends string,
Selectors extends BundleSelectors<Record<string, any>>,
> {
name: Name
selectors: Selectors
}
type BundleInitializer = <
Name extends string,
Selectors extends BundleSelectors<Record<string, any>>,
>(
content: {
name: Name
selectors: Selectors
}
) => Bundle<Name, Selectors>
declare const Init: BundleInitializer
const bun = Init({
name: "Foo", selectors: {
wrongname: 'wrongtype', // CASE 1 (name incorrect, type incorrect)
selectCorrectName: 'wrongtype', // CASE 2 (name correct, type incorrect)
wrongName: () => { }, // CASE 3 (name incorrect, type correct)
selectCorrectAll: () => { }, // CASE 4 (name correct, type correct)
}
})
我们应该如何定义BundleSelectors类型以确保涵盖所有4种情况, 我试过这两个版本,每个版本都有自己的问题,但都不能完全工作
版本1
type BundleSelectors<T> = { [I in keyof T]: Selector }
版本2
此处缺少类型参数,如果要判断类型,只需添加它即可,它已不再使用,但其他类型需要更新以反映
type BundleSelectors = Record<`select${string}`, Selector>
这两种做法都没有产生预期的效果.