首页 > 解决方案 > 使用动态键访问具有不同值类型的对象时,是否可以缩小对象值类型?

问题描述

最小测试用例如下(TS游乐场链接

interface TestObject {
    a: string
    b: number
}

type TestKeyType = keyof TestObject

const myObjct = {a: "cat", b:34} as TestObject;
const key = 'a' as TestKeyType;

if (typeof myObjct[key] !== 'string') {
    throw TypeError();
}

// Error: Type 'string' is not assignable to type 'never'.
myObjct[key] = 'dog';

本质上,我有一个配置对象,其中的值可以是各种类型。当我更新这个对象时,键取决于用户输入,但根据输入的类别,我可以知道它是布尔值还是数字或字符串等。

如图所示,我可以编写一个类型保护,它应该保证动态键对应的类型,但 Typescript 不同意。

我还能做些什么简洁的事情来向 Typescript 证明我的类型是有效的?

我知道我可以枚举对应于每个值类型的键来检查,但这似乎有点冗长和多余;在扩展对象中的键时,我希望尽量减少需要更新的地方。

标签: typescript

解决方案


你可以为此编写一个用户定义的类型保护:typeCheck(obj, key, type)会告诉你是否obj[key]有 type type。皱纹是type必须作为可以在运行时返回的字符串传递typeof,因此您需要从这些字符串到 Typescript 类型的映射;此映射在Primitive下面命名。必要时将您需要的任何其他内容添加到此映射中(例如bigint)。

type Primitive = {
    number: number,
    string: string,
    boolean: boolean,
}
type KeyAssignableTo<T, V> = {[K in keyof T]: T[K] extends V ? K : never}[keyof T]

function typeCheck<T, P extends keyof Primitive>(obj: T, key: keyof T, type: P): key is KeyAssignableTo<T, Primitive[P]> {
    return typeof obj[key] === type;
}

用法:

if(!typeCheck(myObject, key, 'string')) {
    throw TypeError();
}

// key: 'a'
myObject[key] = 'dog';

游乐场链接


推荐阅读