gpt4 book ai didi

typescript - 如何在 Typescript 中编写严格要求其参数的函数类型

转载 作者:搜寻专家 更新时间:2023-10-30 20:57:27 25 4
gpt4 key购买 nike

我遇到了 combineReducers 不够严格的问题,我不确定如何解决它:

interface Action {
type: any;
}

type Reducer<S> = (state: S, action: Action) => S;

const reducer: Reducer<string> = (state: string, action: Action) => state;

const reducerCreator = (n: number): Reducer<string> => reducer;

interface ReducersMapObject {
[key: string]: Reducer<any>;
}

const reducerMap: ReducersMapObject = {
test: reducer,
test2: reducerCreator
}

我预计 reducerMap 会抛出错误,因为 reducerCreator 不是 reducer(它是一个接受字符串并返回 reducer 的函数),但 TypeScript 对此没有问题。

问题的根源似乎是 Reducer 本质上归结为 any => any 因为 functions with fewer parameters are assignable to functions that take more params ..

这意味着 ReducersMapObject 类型基本上只是 {[key: string]: function}

有没有办法让 Reducer 类型更严格地要求这两个参数,或者有其他方法来更加确信 ReducersMapObject 实际上包含 reducer 函数?

这段代码全部编译在TypeScript playground中如果你想复制

最佳答案

好问题...在这个相当长的答案的末尾有两个可行的选择。你问了两个问题,我分别回答了。

问题一:

Is there a way to make the Reducer type stricter about requiring both parameters...

由于 TypeScript 函数中的两个障碍,这将很难实现。

障碍一:丢弃函数参数

您已经注意到的一个障碍,is documented here在“比较两个函数”标题下。它说“我们允许‘丢弃’参数”。即,参数较少的函数可分配给参数较多的函数。 rationale is in the FAQ .简而言之,下面的赋值是安全的,因为参数较少的函数“可以安全地忽略额外的参数”。

const add: (x: number, y: number) = 
(x: number) => { return x; }; // works and is safe

障碍2:函数参数双方差

第二个障碍是 function parameters are bivariant .那 意味着我们不能使用用户定义的类型参数来解决这个问题。在某些语言中,我们可以定义 Pair以及接受 Pair 的函数.

class Pair {
x: number;
y: number;
}

let addPair: (p: Pair) => number;

在具有协变函数的语言中,以上将参数限制为 Pair 的子类型.

在 TypeScript 中,简单的类型分配遵循预期 substitutability rules ,但函数遵循双变规则。在其简单的类型分配中,TypeScript 允许我们分配类型 Pair输入 Single但不分配类型 Single输入 Pair .这是预期的替换。

class Single {
x: number;
}

let s: Single = new Pair(); // works
let p: Pair = new Single(); // fails because of a missing property.

不过,TypeScript 函数是双变的,并且不受相同的限制。

let addSingle: (s: Single) => number; 
addSingle = (p: Pair) => p.x + p.y; // as expected, Pair is assignable to Single.

let addPair: (p: Pair) => number;
addPair = (s: Single) => s.x; // surprise, Single is assignable to Pair!

结果是一个需要 Pair 的函数将接受 Single .

Reducers 的影响

以下两种技术都不会强制执行 Reducer 的参数(或类属性)数量实现必须接受。

class Action { }

// no restriction - TypeScript allows discarding function parameters
type Reducer01<S> = (state: S, action: Action) => S;
const reducer01: Reducer01<number> = (state: number) => 0; // works

// no restriction - TypeScript functions have parameter bivariance
class ReducerArgs<S> {
state: S;
action: Action;
}
type Reducer02<S> = (args: ReducerArgs<S>) => S;
const reducer02 = (args: { state: number }) => 0; // works

实际上这可能不是问题,因为让 ReducersMapObject接受 Reducer参数较少是安全的。编译器仍将确保:

  1. 每次调用 Reducer包括所有 Reducer参数,和
  2. 每次执行 Reducer仅对其(可能很短)参数列表进行操作。

问题2:

...or another way to get more confidence that the ReducersMapObject actually contains reducer functions?

我们正在尝试做的一件事是制作 reducerCreatorReducer<S> 不兼容的函数(以及其他异常形状的函数)函数类型。这里有两个可行的选择。

可行方案一:用户自定义参数类型

上面的第二种技术,使用名为 ReducerArgs<S> 的用户定义类型,会给我们更多信心。它不会提供完全的信心,因为我们仍然会有双方差,但它会确保编译器拒绝 reducerCreator .它可能是这样的:

interface Action {
type: any;
}

// define an interface as the parameter for a Reducer<S> function
interface ReducerArgs<S> {
state: S;
action: Action
}

type Reducer<S> = (args: ReducerArgs<S>) => S;

const reducer: Reducer<string> = (args: ReducerArgs<string>) => args.state;

const reducerCreator = (n: number): Reducer<string> => reducer;

interface ReducersMapObject {
[key: string]: Reducer<any>;
}

const reducerMap: ReducersMapObject = {
test: reducer,
test2: reducerCreator // error!
}

可行的选项 2:泛型和联合类型

另一种选择是使用通用的 ReducerMapObject<T>像这样:

interface ReducersMapObject<T> {
[key: string]: Reducer<T>;
}

然后用a union type参数化它列出所有 reducer 的类型。

const reducer: Reducer<string> = (state: string, action: Action) => state;
const reducer1: Reducer<number> = (state: number, action: Action) => state;

const reducerMap: ReducersMapObject<string | number> = {
test: reducer,
test1: reducer1,
test2: reducerCreator // error!
}

结果将是 any => any变成 T => T , 其中T是联合中列出的类型之一。 (顺便说一句,如果有一个类型表示“x 可以是任何类型,只要它与 y 是同一类型即可。”)

虽然以上两个都涉及更多代码并且有点笨拙,但它们确实可以满足您的目的。这是一个有趣的研究项目。谢谢你的提问!

关于typescript - 如何在 Typescript 中编写严格要求其参数的函数类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45899039/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com