下面是我的代码
async getAll(): Promise<GetAllUserData[]> {
return await dbQuery(); // dbQuery returns User[]
}
class User {
id: number;
name: string;
}
class GetAllUserData{
id: number;
}
getAll
函数返回User[]
,数组的每个元素都具有name
属性,即使其返回类型为GetAllUserData[]
。
我想知道是否可以在TypeScript中"开箱即用"地将对象限制为仅由其类型指定的属性。
9条答案
按热度按时间t98cgbkg1#
我找到了一种方法,使用自TypeScript第3版以来可用的内置类型来确保传递给函数的对象不包含指定(对象)类型之外的任何属性。
9wbgstp72#
Typescript无法限制额外属性
不幸的是这在Typescript中是不可能的,并且在某种程度上与TS类型检查的形状特性相矛盾。
这个线程中依赖于通用
NoExtraProperties
的答案非常优雅,但不幸的是,它们不可靠,并且可能导致难以检测的bug。我将用GregL的答案来演示。
如果在传递一个对象到
thisWorks
/thisIsAsGoodAsICanGet
的时候TS识别出这个对象有额外的属性,那么这个方法就可以工作,但是在TS中,如果它不是一个对象字面量,那么一个值总是可以有额外的属性:因此,在
thisWorks
/thisIsAsGoodAsICanGetIt
中,你不能相信animal param没有额外的属性。溶液
只需使用拾取(Lodash,Ramda,下划线)。
uz75evzq3#
Typescript uses structural typing instead of nominal typing to determine type equality.这意味着类型定义实际上只是该类型对象的“形状”,也意味着共享另一个类型“形状”子集的任何类型都隐含地是该类型的子类。
在您的示例中,因为
User
具有GetAllUserData
的所有属性,所以User
隐式地是GetAllUserData
的子类型。要解决这个问题,你可以添加一个伪属性来使你的两个类彼此不同。这种类型的属性叫做鉴别器。(搜索鉴别联合here)。
您的代码可能如下所示。discriminator属性的名称并不重要。这样做将产生您想要的类型检查错误。
mccptt674#
我不认为这是可能的代码结构。Typescript * 确实 * 有多余的属性检查,这听起来像你所追求的,但他们只工作的对象字面量。从这些文档:
对象文本会得到特殊处理,在将其赋给其他变量或将其作为参数传递时,会进行额外的属性检查。
但是返回的变量不会接受这种检查。
会产生错误“Object literal可能只指定已知属性”,代码:
不会产生任何错误,因为它返回的是变量而不是对象文本本身。
因此,对于您的情况,由于
getAll
不返回文本,typescript不会执行多余的属性检查。41zrol4v5#
在GregL的answer之后,我想添加对数组的支持,并确保如果您有一个数组,则数组中的所有对象都没有额外的props:
注意:只有当你有TS 3. 7(包含)或更高版本时,类型递归才是可能的。
chhqkbe16#
带鉴别器的公认答案是正确的。TypeScript使用结构类型而不是名义类型。这意味着转发器将检查结构是否匹配。由于两个类(可以是接口或类型)都有
number
类型的id
,因此它匹配,因此可以互换(这是正确的一面,因为User
有更多的属性。虽然这可能已经足够好了,但问题是在运行时,从方法
getAll
返回的数据将包含name
属性。返回更多可能不是问题,但如果您将信息发送回其他地方,则可能是问题。如果你想把数据限制在类中定义的范围内(接口或类型),你必须手工构建或扩展一个新的对象。下面是它如何查找你的例子:
如果不使用修剪字段的运行时方法,您可以向TypeScript指示这两个类不同,具有私有字段。当返回类型设置为
GetAllUserData
时,下面的代码不允许您返回User
h7appiyu7#
我发现了另一种解决方法:
查看原始帖子
Playground链接
ukdjmx9f8#
作为一种选择,你可以去与黑客:
此操作产生的错误:
4xy9mtcn9#
当使用类型而不是接口时,属性受到限制。至少在IDE中是这样(没有运行时检查)。
示例
它抛出:
Type '{ x: number; y: number; z: number; }' is not assignable to type 'Point'. Object literal may only specify known properties, and 'z' does not exist in type 'Point'.
我认为与接口相比,类型对于定义封闭的数据结构是很好的。当数据与形状不完全匹配时,让IDE(实际上是编译器)大喊大叫已经是开发时一个很好的类型守护者