我找到的最接近的一个是Typescript fails to check nested object on function return type,但在我的 case 中不涉及任何退货. 我错过了什么基本的东西吗?动态键或TypeScrip如何处理具有额外属性的对象的形状?

处理这个问题的正确方法是什么,这样我才能确保SpecKey参数的定义是正确的.以便我需要删除random,否则将出现TS错误.

完整代码:

type TestObj = {
    valid?: string;
}

type SpecKey = "valid" | "random";

const objA: TestObj = {
    valid: "something",
}

function testFunc(key: SpecKey) {
    const objC: TestObj = {
        ...objA,
// this is ok even the SpecKey could be "invalid" during the runtime 
// even we have the object type annotated
        [key]: 123123213,
    }
}

playground :https://www.typescriptlang.org/play?ts=5.2.2&ssl=23&ssc=2&pln=1&pc=1#code/C4TwDgpgBAKhDOwDyAjAVlAvFA3gKCkKgDcBDAGwEsATAfgC4pEAnSgOwHMBuPAXzzyhIUAMqQAxgGkIILFABEZKtXlQAPguak21APYBbeTzzjdbRFF3oAgoziJUGbPiIkKNRvPgGIwABbsHPIANHwCpubATBLSIIxiEFIycgCypP4AdFo6BgAUAJRQADxQAAwZAKxQtApKNKqe2XqGxhEWVmgAQnYIyOhyLkQZwx3Woa4A2vAxMgC6jACMAEwAzMsrSwsrofx4AGYArmziwJRmUMC9AGJH4rkA1jLxMyCFg4RtUR0Awj0O-c4CK4hiMbONgYQJo8QPMoOt1pttkDCPxeEA

推荐答案

有几个关于如何处理computed property keys个Typescript 的问题需要解决,才能给你预期的行为.一个是microsoft/TypeScript#36920,其中计算的属性永远不会被视为excess properties,另一个是microsoft/TypeScript#38663,在microsoft/TypeScript#38663中,如果union类型的计算键与已知属性键部分冲突,它们最终将被完全忽略.这两个问题都被认为是功能请求(而不是错误,尽管后者会导致不健全).

额外的属性判断更像是Linter特性,而不是类型安全特性,并且不会应用到所有地方.在某种意义上,它们与文字类型系统的一个基本属性不一致:当您通过添加额外属性来为一个类型添加extend个属性时,新类型与旧类型仍然是structurally compatible.因此,在许多情况下,允许拥有过多的房产.它们只在涉及object literals的特定场景中被认为是错误,显然计算的属性不是这样的场景.也许它们应该是,就像微软/TypeScrip#36920建议的那样,但它们不是.所以在您的代码中,将random添加为键的可能性不会引发任何警告.哦,好吧.这就解释了为什么{ valid?: string, random: number }是允许的.

可能更有问题的是,联合类型的计算(computed)属性键一直被加宽到string index signature(请参见microsoft/TypeScript#13948),然后在与其他属性组合时被静默地dropped.因此,const objB = { ...objA, [specKey]: 123123213 }的类型只有{ valid?: string | undefined; },而不是预期的{ valid?: string, random: number } | { valid: number }.我们知道{ valid?: string, random: number }TestObj在 struct 上是兼容的,但{ valid: number }certainly not.没有人警告我们,我们可能会将number分配给预期为string的房产.这就是微软/TypeScrip#38663的建议.如果你想看到这一点发生,你可能想go 那个问题上,给它一个?.在实现这一点之前,您将需要绕过它.


我倾向于为索引签名问题使用的一种解决方法是将计算的属性键创建包装在一个帮助器函数中,该函数提供我所期望的类型.参见MICROSOFT/TypeScrip#13948上的第this comment页.例如,就像这样:

function kv<K extends PropertyKey, V>(k: K, v: V): { [P in K]: { [Q in P]: V } }[K] {
    return { [k]: v } as any
}

然后,我写成{...kv(k, v)},而不是字面上的{[k]: v}:

function testFunc(key: SpecKey) {
    const objC: TestObj = { ...objA, ...kv(key, 123123123) }; // error!
    // Type '{ valid: number; } | { random: number; valid?: string | undefined; }' 
    // is not assignable to type 'TestObj'
}

现在,您得到了预期的错误,具体地说,您正试图将类型为{ valid: number; } | { random: number; valid?: string }的值赋给类型为{ valid?: string }的变量,这是不允许的,因为number不是string.

Playground link to code

Typescript相关问答推荐

如何使用axios在前端显示错误体

使某些类型的字段变为可选字段'

如何在排版中正确键入中间件链和控制器链

如何通过TypeScript中的工厂函数将区分的联合映射到具体的类型(如类)?

Vue SFC中的多个脚本块共享导入的符号

如何按不能保证的功能过滤按键?

在包含泛型的类型脚本中引用递归类型A<;-&B

为什么&;(交集)运算符会删除不相交的属性?

如何 bootstrap TS编译器为类型化属性`T`推断正确的类型?

将具有Readonly数组属性的对象接口转换为数组

下载量怎么会超过下载量?

专用路由组件不能从挂钩推断类型

我怎样才能正确地输入这个函数,使它在没有提供正确的参数时出错

为什么上下文类型在`fn|curred(Fn)`的联合中不起作用?

tailwind css未被应用-react

为什么数字是`{[key:string]:UNKNOWN;}`的有效密钥?

如何在Vuetify 3中导入TypeScript类型?

从泛型对象数组构造映射类型

Angular 16:无法覆盖;baseUrl;在tsconfig.app.json中

TypeScript 中的通用函数包装和类型推断