TypeScript如何从包含不同类型的数组中推断类型?
TypeScript如何从包含不同类型的数组中推断类型?
我正在尝试使用包含多种不同类型的数组,并希望TypeScript可以推断出每个元素的正确类型:
type House = { street: string; zipCode: number; } type Car = { make: string; model: string; year: number } const things: (House | Car)[] = [{ street: '123 Fake Street', zipCode: 12345 }, { make: 'Tesla', model: 'Model X', year: 2022 }, { /* 其他任何 'Car' 或 'House' */ }] things[1].year; // <---- 为什么TypeScript不知道它是一个"Car"类型并在这里显示错误?
有没有一种简单的方法可以帮助TypeScript知道类型,而不需要显式地转换数组元素?
TypeScript如何从包含不同类型的数组中推断类型?
问题的出现的原因是,由于数组的类型被定义为(House | Car)[]
,编译器允许你在数组中添加任意多个House或Car,而且可以在任何位置添加。因此,在编译步骤中,我们无法确定特定索引位置的项是House还是Car。
然而,编译器确实可以通过在运行时检查是否存在只存在于可能类型之一上的属性来推断对象的类型。
在你的例子中,只有Car有make属性,而House没有。因此,通过检查"make" in item
可以确认item
是一个Car。
const item: House | Car = things[1]; if ("make" in item) { // 编译器在此块内满足item是一个Car, // 因为只有Car有"make"属性 item.year // <-- 没有错误 }
这里还有一个有用的模式,叫做"discriminated union"。你可以这样声明你的类型:
type House = { kind: "house"; street: string; zipCode: number; } type Car = { kind: "car"; make: string; model: string; year: number }
通过检查kind
属性的值,编译器可以确定你正在查看的是哪种类型的项:
const item: House | Car = things[1]; if (item.kind === "car") { item.year // <-- 没有错误 } else { item.street // <-- 没有错误 }
TypeScript可以从包含不同类型的数组中推断出类型的原因是因为它具有强大的类型推断功能。当我们将不同类型的对象放入数组中时,TypeScript可以根据对象的属性和方法的类型来推断出数组的类型。
解决方法有两种推荐的方式:元组类型和const断言。元组类型可以在定义数组时指定每个元素的类型,并且可以根据元组中的元素来推断出数组的类型。而const断言可以将数组中的每个对象都标记为只读,从而使TypeScript能够推断出数组的类型。
在给定的示例代码中,我们可以看到两种方法的应用。首先,我们定义了一个元组类型`[House, Car]`,并将其用于`thingsA`数组的类型推断。然后,我们可以通过访问`thingsA`数组的索引来分别将其赋值给`aHouse`和`aCar`变量,并且TypeScript可以正确地推断出它们的类型。如果我们尝试将`thingsA`数组的第三个元素赋值给`aError`变量,TypeScript会报错,提示我们索引超出范围。
接下来,我们使用const断言将`thingsB`数组的类型推断为`readonly [House, Car]`。然后,我们可以将`thingsB`数组的元素分别赋值给`bHouse`和`bCar`变量,并且TypeScript可以正确地推断出它们的类型。如果我们将`thingsB`数组的第一个元素赋值给`bError`变量,TypeScript会报错,提示类型不匹配。
除了元组类型和const断言,我们还可以使用标记联合类型来处理这种情况。在给定的示例代码中,我们定义了`House`和`Car`两个类型,并使用`_tag`属性来标记不同的对象类型。然后,我们将`House`和`Car`作为联合类型的元素,并将其赋值给`thingsA`数组。通过使用switch语句和`_tag`属性,我们可以在循环中根据对象的类型执行相应的操作。
TypeScript具有强大的类型推断功能,可以根据数组中的对象的属性和方法的类型推断出数组的类型。我们可以使用元组类型、const断言或标记联合类型来处理包含不同类型的数组,并使TypeScript能够正确地推断出类型。
在这个问题中,我们想要了解TypeScript如何从包含不同类型的数组中推断类型。如果我们给它一点帮助并对其进行类型检查,它就可以推断出类型。
如果我们使用类型保护来检查类型,就像这个例子中所示,那么TypeScript将正确理解类型,并且甚至在IDE中提供属性的下拉列表。
function isSpecificType(item: House | Car): item is Car { return true; } if (isSpecificType(things[1])) { console.log(things[1].year) } else { console.log(things[1].street) }
根据这里的信息:Switch for specific type in TypeScript
在这里还有一些关于类型保护的有用信息:https://www.typescriptlang.org/docs/handbook/advanced-types.html
我没有给它投票,但这似乎并没有回答这个问题。原贴想要编译器自动推断类型,这对于无界数组来说是不可能的。这将是接近最佳的权衡解决方案。
谢谢!是的,这取决于问题的解释方式。我理解`How can TypeScript infer types from an array containing different types?`为想要找到一种方法在任何元素上调用`*.year`。我相信这个解决方案将满足这个要求。 🙂
类型保护实际上并不是推断,而是带有逻辑的类型断言。但是,是的,目前这是你能做的最好的事情。