gpt4 book ai didi

TypeScript 联合函数类型

转载 作者:行者123 更新时间:2023-12-01 03:05:06 24 4
gpt4 key购买 nike

我正在尝试为箭头函数创建联合类型。目标是能够根据第一个参数推断第二个参数的类型。

理想情况下我想要 y类型为 number | string , 和 z成为 'first' | 'second' .但是在缩小y的类型之后或 z , 自动推断其他参数的缩小类型。

不幸的是,TypeScript 似乎无法处理这样的复杂案例,但我想知道你们中是否有人遇到过类似的问题。

在简化的场景中,我的代码如下所示:

type Callback<T1, T2> = (y: T1, z: T2) => void;

const test = (x: Callback<number, 'first'> | Callback<string, 'second'>) => {
return;
}

// Parameter 'y' implicitly has an 'any' type.
// Parameter 'z' implicitly has an 'any' type.
test((y, z) => {
if(typeof y === 'number') {
// y is a number
// so z must be 'first'
} else {
// y is a string
// so z must be 'second'
}
});

谢谢!

最佳答案

这就是我所看到的。让我们使用这些定义:

type Callback<T1, T2> = (y: T1, z: T2) => void;
type First = Callback<number, 'first'>;
type Second = Callback<string, 'second'>;

首先,我绝对怀疑您想要函数的联合而不是函数的交集。请注意,这样的函数联合本质上是无用的:
const unionTest = (x: First | Second) => {
// x is *either* a First *or* it is a Second,
// *but we don't know which one*. So how can we ever call it?

x(1, "first"); // error!
// Argument of type '1' is not assignable to parameter of type 'never'.
x("2", "second"); // error!
// Argument of type '"2"' is not assignable to parameter of type 'never'.
}
unionTest()功能与您的 test() 相同,但它不能用 x 做任何事情,这只是已知的 FirstSecond .如果你尝试调用它,无论如何你都会得到一个错误。函数的联合只能安全地作用于它们的参数的交集。对此的一些支持是 added in TS3.3 ,但在这种情况下,参数类型是互斥的,因此只有可接受的参数类型为 never ... 所以 x是无法调用的。

我怀疑这种相互不兼容的功能的联合是任何人想要的。并集和交集的对偶性和 contravariance函数类型相对于它们的参数类型令人困惑且难以谈论,但区别很重要,所以我觉得这一点值得详细讨论。这个工会就像发现我必须安排一个周一有空或周二有空的人会面,但我不知道是哪一个。我想如果我能在星期一和星期二举行 session ,那会奏效,但假设这没有意义,我就被卡住了。我会见的人是工会,而我会见的那天是十字路口。做不到。

相反,我认为您想要的是函数的交集。这对应于 overloaded功能;你可以两种方式调用它。看起来像这样:
const intersectionTest = (x: First & Second) => {
// x is *both* a First *and* a Second, so we can call it either way:
x(1, "first"); // okay!
x("2", "second"); // okay!
// but not in an illegal way:
x(1, "second"); // error, as desired
x("2", "first"); // error, as desired
}

现在我们知道 x都是 First和一个 Second .你可以看到你可以把它当作 First或像 Second并且没事。但是,您不能将其视为某种奇怪的混合体,例如 x(1, "second") ,但大概这就是你想要的。现在我正在安排与周一和周二都有空的人会面。如果我问那个人在哪一天安排 session ,她可能会说“周一或周二我都可以”。开会那天是一个联合,而我会见的人是一个交集。那个有效。

所以现在我假设您正在处理函数的交集。不幸的是编译器 doesn't automatically synthesize the union of parameter types for you ,并且您仍然会遇到“隐含任何”错误。
// unfortunately we still have the implicitAny problem:
intersectionTest((x, y) => { }) // error! x, y implicitly any

您可以手动将函数的交集转换为作用于参数类型联合的单个函数。但是对于两个受约束的参数,表达这一点的唯一方法是使用 rest arguments and rest tuples .我们可以这样做:
const equivalentToIntersectionTest = (
x: (...[y, z]: Parameters<First> | Parameters<Second>) => void
) => {
// x is *both* a First *and* a Second, so we can call it either way:
x(1, "first"); // okay!
x("2", "second"); // okay!
// but not in an illegal way:
x(1, "second"); // error, as desired
x("2", "first"); // error, as desired
}

这与 intersectionTest() 相同就其行为方式而言,但现在参数具有已知的类型,并且可以在上下文中输入比 any 更好的类型。 :
equivalentToIntersectionTest((y, z) => {
// y is string | number
// z is 'first' | 'second'
// relationship gone
if (z === 'first') {
y.toFixed(); // error!
}
})

不幸的是,正如您在上面看到的,如果您使用 (y, z) => {...} 实现该回调, y的种类和 z成为独立的工会。编译器忘记了它们是相互关联的。一旦您将参数列表视为单独的参数,就会失去相关性。我已经看到足够多的问题想要解决这个问题,我 filed an issue about it ,但目前没有直接支持。

让我们看看如果我们不立即分离参数列表会发生什么,通过将其余参数分散到一个数组中并使用它:
equivalentToIntersectionTest((...yz) => {
// yz is [number, "first"] | [string, "second"], relationship preserved!

嗯,很好。现在 yz仍在跟踪约束。

这里的下一步是尝试缩小 yz通过类型保护测试连接到工会的一个或另一条腿。最简单的方法是如果 yzdiscriminated union .确实如此,但不是因为 y (或 yz[0])。 numberstring不是文字类型,不能直接用作判别式:
  if (typeof yz[0] === "number") {
yz[1]; // *still* 'first' | 'second'.
}

如果一定要查 yz[0] ,您必须实现自己的 type guard function支持这一点。相反,我建议打开 z (或 yz[1]),因为 "first""second"是可用于区分联合的字符串文字:
  if (yz[1] === 'first') {
// you can only destructure into y and z *after* the test
const [y, z] = yz;
y.toFixed(); // okay
z === "first"; // okay
} else {
const [y, z] = yz;
y.toUpperCase(); // okay
z === "second"; // okay
}
});

请注意,在 yz[1] 之后已与 'first' 进行比较, yz的类型不再是联合,因此您可以解构为 yz以更有用的方式。

好吧,嗬。好多啊。 TL;DR 代码:
const test = (
x: (...[y, z]: [number, "first"] | [string, "second"]) => void
) => { }

test((...yz) => {
if (yz[1] === 'first') {
const [y, z] = yz;
y.toFixed();
} else {
const [y, z] = yz;
y.toUpperCase(); // okay
}
});

希望有所帮助;祝你好运!

Link to code

关于TypeScript 联合函数类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58629426/

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