javascript - 从方法对象获取窄类型

标签 javascript typescript object types narrowing

我想得到一个像这样的 Narrowing 类型:

type Expected = {
    method: 'firstNamespace/firstMethod'
    payload: [string]
} | {
    method: 'firstNamespace/secondMethod'
    payload: [number, number]
} | {
    method: 'secondNamespace/firstMethod'
    payload: [number]
}
从这样的方法的对象:
const Methods = {
    firstNamespace: {
        firstMethod: (p: string) => {},
        secondMethod: (p: number, k: number) => {}
    },
    secondNamespace: {
        firstMethod: (p: number) => {}
    }
}
我尝试了各种方法,但我认为我在 TypeScript 中有一些误解。
我已经使用 TypeScript 快一年了,任何超出基本类型的东西似乎都超出了我的能力范围......

最佳答案

这可以通过 recursive conditional types 在 TypeScript 中实现深入了解 Methods 的嵌套属性, 和 template literal types在类型级别将方法名称连接在一起。
我们将您要查找的类型函数称为 MethodsToExpected<T> ,它采用对象类型 T并产生 union对象类型的 method属性是 "/" - 每个方法名称的分隔路径,其payload属性是 tuple对应方法的参数类型。然后我们可以定义MethodsToExpected<T>就自身而言,递归地是这样的:

type MethodsToExpected<T> = { [K in keyof T]-?:
    T[K] extends (...args: infer P) => any ? { method: K, payload: P } :
    MethodsToExpected<T[K]> extends infer X ? (
        X extends { method: infer M, payload: infer P } ? (
            { method: `${Extract<K, string>}/${Extract<M, string>}`; payload: P }
        ) : never
    ) : never
}[keyof T]
施工{[K in keyof T]-?: XXX}[keyof T] ,立即indexes into一个 mapped type用它的键,产生所有 XXX 的联合类型。
对于每个属性键 K来自 T ,我们检查属性类型T[K] .如果它是一个函数类型,那么我们获取它的参数列表并立即返回{method: K, payload: P} .否则我们申请MethodsToExpected<T[K]>到属性(property)并进行检查。
(旁白:我正在使用 conditional type inferenceMethodsToExpected<T[K]> “复制”到新的类型参数 X 中,然后我将其用作 distributive conditional type 中的检查类型,以便 X 中的任何联合都分发到最终的联合。如果您只是使用 MethodsToExpected<T[K]> extends {method: infer M, payload: infer P} 而不是中间 X ,它将产生类似 {method: "a/b" | "a/c", payload: [string] | [number]} 而不是所需的 {method: "a/b", payload: [string]} | {method: "a/c", payload: [number]} 的东西。)
对于 MethodsToExpected<T[K]> 的每个联合元素,我们拔出method类型 Mpayload类型 P , 并建立一个新的 method/payload一对。 payload类型不会改变,只是 P , 但我们在当前 key K 前面加上和斜线 "/"M使用模板文字类型。编译器不能确定 KM都是string类型,所以它不想让你写 `${K}/${M}`直接地。相反,我们使用 the Extract<T, U> utility type让编译器相信我们只会连接 string s。

让我们看看它是否有效:
type Expected = MethodsToExpected<typeof Methods>;
/* type Expected = {
    method: "firstNamespace/firstMethod";
    payload: [p: string];
} | {
    method: "firstNamespace/secondMethod";
    payload: [p: number, k: number];
} | {
    method: "secondNamespace/firstMethod";
    payload: [p: number];
} */
看起来不错。为了确保它深入到嵌套的子属性,让我们尝试一个不同的:
type Nested = MethodsToExpected<{ a: { b: { c: { d: { e: (f: string) => number } } } } }>;
/* type Nested = {
    method: "a/b/c/d/e";
    payload: [f: string];
} */
也很好。
Playground link to code

关于javascript - 从方法对象获取窄类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69562731/

相关文章:

javascript - 将数组中的对象移动到末尾

javascript - render 方法中的对象创建是否会破坏 'React.PureComponent' 并在 'shouldComponentUpdate' 中进行渲染检查?

javascript - Node.js:使用 ImageMagick 时的异步问题

javascript - 检查由 JavaScript 中不相关事件触发的函数中的函数是否完成

javascript - insertMany 总是返回重复的 _id 错误

typescript - 从 Angular 2 中的 Resolver 取消路由导航

javascript - 将对象内容数组分配给新数组

javascript - 在 angular2 应用程序中加载 JavaScript 文件

javascript - Typescript 和 Javascript ES6、ES.NEXT 类构造函数参数对比

带有对象键的 JavaScript setter 和 getter