gpt4 book ai didi

TypeScript 无法确定类型,即使它具有所有必要的信息

转载 作者:行者123 更新时间:2023-12-05 09:29:24 27 4
gpt4 key购买 nike

我在 TypeScript 类型中遇到过这个问题。我在同一接口(interface)中使用基于不同属性的条件类型来确定一个属性的类型。此处,FruitBasket 中的属性 slicerAppleSlicerBananaSlicer,具体取决于 type.

如果您查看函数 test,它接收一个 FruitBasket 实例,它可以是 AppleBanana。因此,我通过检查 type 是否等于其中之一来缩小类型,但它仍然提示 basket.slicer 不是确定性的。但是,它应该具有确定它是 AppleSlicer 所需的所有信息。我该如何解决这个问题?

我正在使用 TypeScript 4.5.4。

enum Fruits {
Apple = "Apple",
Banana = "Banana",
}

// These interfaces can have completely different shapes as shown.
interface AppleSlicer { apple(): void }
interface BananaSlicer { banana(): void }

export type FruitSlicer<TType> = TType extends typeof Fruits.Apple
? AppleSlicer
: TType extends typeof Fruits.Banana
? BananaSlicer
: never

export interface FruitBasket<TType extends Fruits> {
type: TType
slicer: FruitSlicer<TType>
}

const test = (basket: FruitBasket<Fruits>) => {
if (basket.type === Fruits.Apple) {
// This gives me a compile error because basket.slicer is either AppleSlicer or BananaSlicer.
// But, it should have all information it needs to deduce that it can only be an AppleSlicer
basket.slicer.apple()
}
}

最佳答案

这里最大的问题是FruitBasket<Fruits>不是正确的 discriminated union您可以在其中检查判别属性(即 type )以缩小对象的类型。它不仅不是受歧视的工会,甚至不是union。根本。相当于

{ type: Fruits, slicer: AppleSlicer | BananaSlicer }

所以这是一个完全有效的 FruitBasket<Fruits>根据该定义:

const whoops: FruitBasket<Fruits> = {
type: Fruits.Apple,
slicer: { banana() { } }
} // okay

仅仅因为typeFruits.Apple ,这并不意味着 slicer将是 FruitSlicer<Fruits.Apple> .所以如果test()接受 FruitBasket<Fruits> , 然后它接受 whoops :

test(whoops) // no error here either

意味着test()的实现真的不能安全地得出任何特别关于 basket.slicer 的结论通过查看 basket.type .编译器错误是有效的。糟糕。


所以你不想test接受FruitBasket<Fruits> .你要的是联合类型 FruitBasket<Fruits.Apple> | FruitBasket<Fruts.Banana> ,一个受歧视的联合,其中 type是判别性质。

如果您不想手动写出该类型(例如,您的 Fruits 中还有许多其他 enum),您可以从您的 FruitBasket<T> 版本生成此联合如下:

type FruitBasketUnion = { [F in Fruits]: FruitBasket<F> }[Fruits]
// type FruitBasketUnion = FruitBasket<Fruits.Apple> | FruitBasket<Fruits.Banana>

我们在这里创建一个 mapped typeFruitBasket<F>每个属性类型 FFruits ,然后立即 indexing into映射类型为 Fruits以获得所需的联合。

现在我们可以将其设为test() 的参数类型并看到支票basket.type === Fruits.Apple 确实缩小了 basket 的类型至 FruitBasket<Fruits.Apple> :

const test = (basket: FruitBasketUnion) => {
if (basket.type === Fruits.Apple) {
basket.slicer.apple() // okay
}
}

现在 test()实现编译没有错误。那最好意味着你不能调用 test(whoops)不再:

test(whoops); // error!
// ~~~~~~
// Argument of type 'FruitBasket<Fruits>' is not assignable to
// parameter of type 'FruitBasketUnion'.

所以编译器正确地拒绝了whoops .让我们确保它接受 FruitBasket<Fruits.Apple>FruitBasket<Fruits.Banana> :

test({
type: Fruits.Banana,
slicer: { banana() { } }
}); // okay

test({
type: Fruits.Apple,
slicer: { apple() { } }
}); // okay

看起来不错。

Playground link to code

关于TypeScript 无法确定类型,即使它具有所有必要的信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70689820/

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