gpt4 book ai didi

typescript - TS 类型防护带复杂类型

转载 作者:行者123 更新时间:2023-12-03 16:32:54 25 4
gpt4 key购买 nike

我遇到了一个有趣的 TS 类型守卫的行为,想问问你是我必须接受它还是我做错了什么。我把最小的例子放在一起(嗯,接近最小)。

enum LegType {
LegA = 'LegA',
LegB = 'LegB',
}

interface LegA {
type: LegType.LegA
foo: string
}

interface LegB {
type: LegType.LegB
bar: string
}

type Leg = LegA | LegB

enum PlanType {
T = 'T',
C = 'C',
}

interface TPlan {
type: PlanType.T
legs: LegA[]
}

interface CPlan {
type: PlanType.C
legs: (LegA | LegB)[]
}

type Plan = TPlan | CPlan

const isLegA = (o: Leg): o is LegA => o.type === LegType.LegA

const getFoo = (plan: Plan): string | undefined => {
// const legs: Leg[] = plan.legs // With this line the code builds with no errors.
const legs = plan.legs // With this line the code contains error, the type of `legs` is resolved as: LegA[] | (LegA | LegB)[]
const someLegA = legs.find(isLegA)
return someLegA?.foo // Property 'foo' does not exist on type 'LegA | LegB'.
}

TS playground

为什么需要添加 :Leg[]?是否可以重写代码以便 TS 自己理解正确的类型是什么?

最佳答案

糟糕,我明白发生了什么,我认为这里唯一合理的答案是做你所做的:注释 legs作为Leg[] .

因此,library type signature对于 Arrayfind()方法是 overloaded和接受 user-defined type guard 的重载回调并返回一个缩小的元素类型是 generic :

find<S extends T>(
predicate: (this: void, value: T, index: number, obj: T[]) => value is S, thisArg?: any
): S | undefined;

find(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): T | undefined;

当你不注释时 legs , 它得到 union type Array<Leg> | Array<LegA> .此类型被认为与 Array<Leg> 兼容但它不会自动处理它(这可能很好。Array<LegA> 不应该让你 push() 一个 LegB 到它上面,但 Array<Leg> 应该)。

这意味着 legs.find 的类型本身是签名不相同的函数的联合:

type LegsFind = {
<S extends LegA>(p: (this: void, v: LegA, i: number, o: LegA[]) => v is S, t?: any): S | undefined;
(p: (v: LegA, i: number, o: LegA[]) => unknown, t?: any): LegA | undefined;
} | {
<S extends Leg>(p: (this: void, v: Leg, i: number, o: Leg[]) => v is S, t?: any): S | undefined;
(p: (v: Leg, i: number, o: Leg[]) => unknown, t?: any): Leg | undefined;
};

在 TypeScript 3.3 之前,编译器会直接放弃并告诉您它不知道如何调用这样的函数联合。有人会告诉你改变Array<X> | Array<Y>Array<X | Y>如果您正在调用非变异方法,例如 find() , 你会使用 Leg[]继续前进。


引入 TypeScript 3.3 improved behavior for calling union types .在某些情况下,编译器可以采用函数联合并将其表示为采用 intersection 的单个函数。来自联盟每个成员的参数。您可以在相关的拉取请求中阅读更多相关信息,microsoft/TypeScript#29011 .

但是,there are cases when it can't do this :特别是如果联合的多个分支被重载或泛型函数。不幸的是 find() ,这就是你所处的情况。看起来编译器尝试想出一种方法来使它可调用,但它放弃了与用户定义类型保护缩小对应的通用签名并以“常规”结尾,可能是因为它们彼此兼容:

type LegsFind33 = (p: (v: LegA, i: number, o: Leg[]) => unknown, thisArg?: any) => Leg | undefined;

所以你调用成功了,没有发生收缩,你就遇到了问题。调用函数并集的问题没有完全解决;它的 GitHub 问题 microsoft/TypeScript#7294已关闭并标记为“重访”。

所以在重新审视之前,这里的建议仍然对待Array<X> | Array<Y>喜欢Array<X | Y>如果您正在调用非变异方法,例如 find() ... 使用 Leg[]继续前进。


好的,希望对你有帮助;祝你好运!

关于typescript - TS 类型防护带复杂类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60441311/

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