打字稿推断而未列举所有可能性

问题描述 投票:1回答:1

我想知道以下问题是否有解决方案:我有两个接受不同输入参数的函数,我有一个将每个函数映射到字符串的对象。

我定义了一种类型,该类型是对象的联合类型,其中包含映射中一个函数的键以及与函数参数类型匹配的值。给定具有该类型的对象,我想从map对象中检索相应的函数并使用值对其进行调用,但typescript不会进行类型推断。

const f1 = (value: number) => value;
const f2 = (value: string) => value;

const map = {
    f1,
    f2
};

type MyType = {
    fun: "f1";
    value: Parameters<typeof map["f1"]>;
} | {
    fun: "f2";
    value: Parameters<typeof map["f2"]>;
};

// does not work
const test1 = (b: MyType) => {
    map[b.fun].apply(null, b.value);
};

// works
const test2 = (b: MyType) => {
    if (b.fun === "f1") {
        map[b.fun].apply(null, b.value);
    } else {
        map[b.fun].apply(null, b.value);
    }
};

是否有可能做这样的事情而不必枚举开关中的所有可能性或if / else?

typescript type-inference
1个回答
0
投票

您遇到了TypeScript对我所说的correlated record types的不足支持。现在,它看到b.fun是联合类型"f1" | "f2",并且b.value是联合类型[string] | [number],并将其视为unorrelated。通常为a union of functions can only be called with an intersection of its parameters,在这种情况下为[string] & [number]。由于无法将[string] | [number]分配给[string] & [number],因此编译器会抱怨。实际上,没有值可分配给[string] & [number],因为它的第一个元素必须是与string & number等效的never

为了使编译器验证map[b.fun].apply(null, b.value)是类型安全的,它本质上需要多次分析代码,每种b可能类型都需要分析一次。您可以通过在switch / caseif / else语句中复制代码来显式地实现此目的,但是它不会自动发生(这是有道理的,因为这将导致编译时间成倍增加)代码中的联合类型值的数量增加)和you can't even ask the compiler to do it on an opt-in basis

目前,唯一合理的解决方法是,让您比编译器更了解代码的类型安全,并使用type assertion或等效代码告诉编译器不要担心,例如:

type MapVals = typeof map[keyof typeof map];
type LooselyTypedMapFunction = (...x: Parameters<MapVals>) => ReturnType<MapVals>
const test1 = (b: MyType) => {
  (map[b.fun] as LooselyTypedMapFunction).apply(null, b.value);
};

这里,类型LooseLyTypedMapFunction最终将f1f2的类型模糊化为一个接受并产生string | number的函数。然后,我们使用类型断言map[b.fun] as LooselyTypedMapFunction对编译器说谎。我们告诉它map[b.fun]将接受string,并且还将接受number。当然,这是错误的,但是只要您传递了相关的b.value列表,而不是像`[Math.random()<0.5? “ oops”:123])。

该类型断言可能比您想要做的工作还要多,但是如果您将任何太疯狂的内容传递给map[b.fun],则至少它可能会捕获,因为[string] | [number]至少会禁止[{foo: string}]。您可以使用不太安全的断言,例如:

const test2 = (b: MyType) => {
  (map[b.fun] as Function).apply(null, b.value);
};

这不需要LooselyTypedMapFunction的类型变乱,但也可以接受(map[b.fun] as Function).apply(null, [{foo: 123}]);。所以要小心。


好的,希望能给您一些指导。祝你好运!

Playground link to code

© www.soinside.com 2019 - 2024. All rights reserved.