- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我遇到了 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
第二个障碍是 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
参数较少是安全的。编译器仍将确保:
Reducer
包括所有 Reducer
参数,和 Reducer
仅对其(可能很短)参数列表进行操作。...or another way to get more confidence that the ReducersMapObject actually contains reducer functions?
我们正在尝试做的一件事是制作 reducerCreator
与 Reducer<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!
}
另一种选择是使用通用的 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/
我已经写了并且 npm 发布了这个:https://github.com/justin-calleja/pkg-dependents 现在我正在用 Typescript 编写这个包:https://g
我有一个函数,我想在 TypeScript 中模拟它以进行测试。在我的测试中,我只关心 json和 status .但是,当使用 Jest 的 jest.spyOn 时我的模拟函数的类型设置为返回 h
我正在使用一个库 (Axios),它的包中包含 Typescript 声明。 我想声明一个将 AxiosResponse(在库的 .d.ts 文件中声明)作为参数的函数。我有以下内容: functio
我是 Typescript 的新手。我想使用 将一个 Typescript 文件加载到另一个 Typescript 文件中标签。 我做了一些事情,但它不起作用!请帮助我。 first.ts: imp
为什么我会收到下面屏幕截图中显示的错误? Atom 说我的 tsconfig.json“项目文件包含无效选项”用于 allowJs、buildOnSave 和 compileOnSave。 但是应该允
所以我正在创建一个 TypeScript 库,我可以轻松地将所有生成的 JS 文件编译成一个文件。有没有办法将所有 .ts 和 .d.ts 编译成一个 .ts 文件? 除了支持 JS 的版本(较少的智
Microsoft Research 提供了一种名为Safer TypeScript 的新 TypeScript 编译器变体: http://research.microsoft.com/en-us/
我需要这个来在单个文件中分发 TypeScript 中的库。有没有办法将多个 typescript 文件合并到(一个js文件+一个 typescript 定义)文件中? 最佳答案 要创建一个库,您可以
用例:我想知道一个函数在 typescript 中执行需要多少时间。我想为此目的使用装饰器。我希望装饰器应该返回时间以便(我可以进一步使用它),而不仅仅是打印它。 例如: export functio
我想检查一个类型是否可以为 null,以及它是否具有值的条件类型。 我尝试实现 type IsNullable = T extends null ? true : false; 但是好像不行 type
我的问题是基于这个 question and answer 假设我们有下一个代码: const myFn = (p: { a: (n: number) => T, b: (o: T) => v
我知道双重否定前缀,我知道 TypeScript 的单后缀(非空断言)。 但是这个双后缀感叹号是什么? /.*验证码为(\d{6}).*/.exec(email.body!!)!![1] 取自here
我正在使用以下文件结构在 Webstorm 中开发一个项目 | src | ... | many files | types | SomeInterface |
在 TypeScript 类中,可以为属性声明类型,例如: class className { property: string; }; 如何在对象字面量中声明属性的类型? 我试过下面的代码,但它
我正在寻找一种在不丢失推断类型信息的情况下将 TypeScript 中的文字值限制为特定类型的好方法。 让我们考虑一个类型Named,它保证有一个名字。 type Named = { name:
在 TypeScript 中,我想创建一个联合类型来表示属于一个或多个不同类型的值,类似于 oneOf在 OpenAPI或 JSON Schema .根据a previous answer on a
type Func = (foo:string) => void // function expression const myFunctionExpression:Func = function(f
假设我有一个联合类型,我正在使用类似 reducer 的 API 调用模式,看起来像这样: type Action = { request: { action: "create
我在 typescript 中有以下去抖功能: export function debounce( callback: (...args: any[]) => void, wait: numb
在 Vue3 的 defineComponent 函数中,第一个泛型参数是 Props,所以我在这里使用 Typescript 接口(interface)提供我的 props 类型。喜欢: expor
我是一名优秀的程序员,十分优秀!