下面的当前示例仍然不能完全按照我的意愿行事(例如:操作redux减速器的函数)。此函数输出所需类型但允许不需要的输入(c: 'hello'
)。这里似乎真的需要使用currying来按照事物的顺序欺骗编译器。
type Reducer<State> = (s: State, p: any) => State
const reducerMap = <State>(s: State) => <
ScopedReducers extends { [k in keyof State]: Reducer<State[k]> }
>(
input: ScopedReducers
): { [k in keyof ScopedReducers]: Reducer<State> } => {
return undefined as any
}
const state = { a: 1, b: "2" }
const mapped = reducerMap(state)({
a: state => state + 1,
b: state => state + "_",
c: "hello",
})
ScopedReducers
是一个与State
有某种关系的对象(每个键都是Reducer<State[k]>
),但是有自己的一部分(减速器有效负载)。 ScopedReducers
应该只包含State
也有的密钥。ScopedReducers
直接指定给其约束,则其函数输入将正确验证。如果我在State
中放入一个不存在的额外密钥,编译器将会出错,如预期的那样。
payload
部分。此类型将被删除为any
。ScopedReducers
扩展了它的约束,它的类型不会被删除,并且可以在编写动态映射函数返回时使用。
extends
,函数参数现在接受无关键(例如:e : 2
)。关系验证仍适用于适用的密钥。“真正的野兽”是this personal project上这个文件的最后一个函数,它也有一个“keyof”问题,我认为可能无法解决......
答案 0 :(得分:2)
不要过多担心代码中发生的事情以及表面上的问题,我认为您正试图让ScopedReducers
成为所谓的exact type,其中包含完全指定的属性:不少也不多。不幸的是,这种类型还不存在于该语言中。幸运的是,由于conditional types是在TypeScript 2.8中引入的,现在a way要求类型参数的行为类似于确切类型,这就是您需要的所有内容。
让我们介绍Exactify<T, X>
:
type Exactify<T, X extends T> = T & {
[K in keyof X]: K extends keyof T ? X[K] : never
}
并想象一下当某个X
的泛型类型参数Exactify<T, X>
被约束为T
时会发生什么:
declare function exactlyFoo<X extends Exactify<Foo, X>>(x: X): void;
X extends Exactify<Foo, X>
是什么意思?如果X
只包含与Foo
相同的密钥,则X extends Exactify<Foo, X>
仅表示X extends T & X
,只是X extends T
。但是,如果X
甚至在extraKey
中没有一个密钥(比如Foo
),则X extends Exactify<Foo, X>
会变为X extends T & X & { extraKey: never }
,除非属性{{1},否则这是不可能的类型extraKey
。实际上,这意味着您无法添加额外的密钥。
如果我将never
应用于您的代码,则会变为:
Exactify<>
使用它:
const reducerMap = <State>(s: State) => <
ScopedReducers extends Exactify<
{ [k in keyof State]: Reducer<State[k]> }, ScopedReducers>>(
input: ScopedReducers
): { [k in keyof ScopedReducers]: Reducer<State> } => {
return undefined as any
}
您根据需要在属性const state = { a: 1, b: "2" }
const mapped = reducerMap(state)({
a: state => state + 1,
b: state => state + "_",
c: "hello", // error!
})
处收到错误。希望有所帮助。祝你好运!