- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试为适用于可区分联合的 typescript 创建模式匹配函数。
例如:
export type WatcherEvent =
| { code: "START" }
| {
code: "BUNDLE_END";
duration: number;
result: "good" | "bad";
}
| { code: "ERROR"; error: Error };
我希望能够输入
match
看起来像这样的函数:
match("code")({
START: () => ({ type: "START" } as const),
ERROR: ({ error }) => ({ type: "ERROR", error }),
BUNDLE_END: ({ duration, result }) => ({
type: "UPDATE",
duration,
result
})
})({ code: "ERROR", error: new Error("foo") });
到目前为止我有这个
export type NonTagType<A, K extends keyof A, Type extends string> = Omit<
Extract<A, { [k in K]: Type }>,
K
>;
type Matcher<Tag extends string, A extends { [k in Tag]: string }> = {
[K in A[Tag]]: (v: NonTagType<A, Tag, K>) => unknown;
};
export const match = <Tag extends string>(tag: Tag) => <
A extends { [k in Tag]: string }
>(
matcher: Matcher<Tag, A>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) => <R extends any>(v: A): R =>
(matcher as any)[v[tag]](v);
但我不知道如何键入每个案例的返回类型
ERROR: ({ error }) => ({ type: "ERROR", error }), // return type is not inferred presently
那么每个 case like function 的返回类型是未知的,因为
match
的返回类型也是未知的。函数本身:
最佳答案
在我看来,您可以采用两种方法。
1.输入类型事先知道
如果要强制最终函数的初始化采用特定类型,则必须事先知道该类型:
// Other types omitted for clarity:
const match = <T>(tag) => (transforms) => (source) => ...
在本例中,您指定
T
在第一次调用时,结果如下类型约束:
tag
必须是 T
的 key transforms
必须是一个包含 T[typeof tag]
的所有值的键的对象source
必须是 T
类型T
的类型确定
tag
的值,
transforms
和
source
可以有。这对我来说似乎是最简单易懂的,我将尝试为此提供一个示例实现。但在我这样做之前,还有方法2:
source
的类型上有更多的灵活性基于
tag
的值和
transforms
,然后可以在最后一次调用时给出或推断出类型:
const match = (tag) => (transforms) => <T>(source) => ...
在这个例子中
T
在上次调用时实例化,因此具有以下类型约束:
source
必须有 key tag
typeof source[tag]
必须是最多所有 transforms
的联合键,即 keyof typeof transforms
.换句话说,(typeof source[tag]) extends (keyof typeof transforms)
对于给定的 source
必须始终为真. T
的特定替换。 ,但是
T
最终可能是满足上述约束的任何类型。这种方法的一个主要缺点是对
transforms
的类型检查很少。 ,因为它可以有任何形状。
tag
之间的兼容性,
transforms
和
source
只能在最后一次调用后检查,这让事情变得更难理解,任何类型检查错误都可能相当神秘。因此,我将采用下面的第一种方法(而且,这个方法很难理解;)
Record<string, any>
:
const match = <T extends Record<string, any>>(tag: keyof T) => ...
我们为您的示例调用它的方式是:
const result = match<WatcherEvent>('code') (...) (...)
We are going to need the type of
tag
for further building the function, but to parameterise that, e.g. withK
would result in an awkward API where you have to write the key literally twice:const match = <T extends Record<string, any>, K extends keyof T>(tag: K)
const result = match<WatcherEvent, 'code'>('code') (...) (...)So instead I'm going for a compromise where I'll write
typeof tag
instead ofK
further down the line.
transforms
的函数,让我们使用类型参数
U
保持其类型:
const match = <T extends Record<string, any>>(tag: keyof T) => (
<U extends ?>(transforms: U) => ...
)
U
的类型约束是它变得棘手的地方。所以
U
对于
T[typeof tag]
的每个值必须是一个具有一个键的对象,每个键都包含一个转换
WatcherEvent
的函数任何你喜欢的东西(
any
)。但不仅仅是任何
WatcherEvent
,特别是将相应的键作为
code
的值的那个.要输入这个,我们需要一个帮助类型来缩小
WatcherEvent
联合到一个成员。概括这种行为,我想出了以下几点:
// If T extends an object of shape { K: V }, add it to the output:
type Matching<T, K extends keyof T, V> = T extends Record<K, V> ? T : never
// So that Matching<WatcherEvent, 'code', 'ERROR'> == { code: "ERROR"; error: Error }
使用这个助手我们可以编写第二个函数,并填写
U
的类型约束。如下:
const match = <T extends Record<string, any>>(tag: keyof T) => (
<U extends { [V in T[typeof tag]]: (input: Matching<T, typeof tag, V>) => any }>(transforms: U) => ...
)
此约束将确保
transforms
中的所有函数输入签名拟合
T
的推断成员union (或
WatcherEvent
在您的示例中)。
Note that the return type
any
here does not loosen the return type ultimately (because we can infer that later on). It simply means that you're free to return anything you want from functions intransforms
.
source
的函数。 ,它的输入签名非常简单;
S
必须延长
T
,其中
T
是
WatcherEvent
在您的示例中,和
S
将是准确的
const
给定对象的形状。返回类型使用
ReturnType
typescript 标准库的帮助器,用于推断匹配函数的返回类型。实际的函数实现相当于你自己的例子:
const match = <T extends Record<string, any>>(tag: keyof T) => (
<U extends { [V in T[typeof tag]]: (input: Matching<T, typeof tag, V>) => any }>(transforms: U) => (
<S extends T>(source: S): ReturnType<U[S[typeof tag]]> => (
transforms[source[tag]](source)
)
)
)
应该是这样!现在我们可以拨打
match (...) (...)
获取函数
f
我们可以针对不同的输入进行测试:
// Disobeying some common style rules for clarity here ;)
const f = match<WatcherEvent>("code") ({
START : () => ({ type: "START" }),
ERROR : ({ error }) => ({ type: "ERROR", error }),
BUNDLE_END : ({ duration, result }) => ({ type: "UPDATE", duration, result }),
})
并尝试使用不同的
WatcherEvent
成员给出以下结果:
const x = f({ code: 'START' }) // { type: string; }
const y = f({ code: 'BUNDLE_END', duration: 100, result: 'good' }) // { type: string; duration: number; result: "good" | "bad"; }
const z = f({ code: "ERROR", error: new Error("foo") }) // { type: string; error: Error; }
注意,当你给
f
WatcherEvent
(联合类型)而不是文字值,返回的类型也将是转换中所有返回值的联合,这对我来说似乎是正确的行为:
const input: WatcherEvent = { code: 'START' }
const output = f(input)
// typeof output == { type: string; }
// | { type: string; duration: number; result: "good" | "bad"; }
// | { type: string; error: Error; }
最后,如果您需要返回类型中的特定字符串文字而不是通用
string
类型,你可以通过改变你定义为
transforms
的函数来做到这一点。 .例如,您可以定义额外的联合类型,或使用“
as const
” ' 函数实现中的注释。
关于typescript - typescript 中模式匹配函数的返回类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64884948/
我已经写了并且 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
我是一名优秀的程序员,十分优秀!