【问题标题】:How to create a type-safe object that allows querying by any string如何创建允许通过任何字符串查询的类型安全对象
【发布时间】:2021-06-28 11:47:21
【问题描述】:

我想创建一个对象 + getter 函数,如果我们不知道键是否在对象中,它将返回已知值类型(如果键 extends keyof typeof obj)或值类型不是。有点像这样:

const obj = {
  a: 1,
  b: 2,
  c: 3
}

const a = obj.a
// typeof a = number

const b = obj['b']
// typeof b = number

let key: string = 'anything'
const d = obj[key]
// typeof d = number | undefined
if (d) {
  // typeof d = number
} else {
  // typeof d = undefined
}

在现实中发生的事情是

let key: string = 'anything'
const d = obj[key]
// TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ a: number; b: number; c: number; }'.   No index signature with a parameter of type 'string' was found on type '{ a: number; b: number; c: number; }'.

I've been trying to create a `get` function which would work this way, but can't get it to work. The result type is always either `any` or `unknown`.

【问题讨论】:

  • 我们怎么会不知道key是否存在于一个常量对象中呢?

标签: javascript typescript typescript-typings type-safety typescript-types


【解决方案1】:

在我看来,TypeScript 对这种事情有点不满意。但是,我认为通常的做法是编写类型保护。类似于下面的东西。 isObjKey 显式检查实际的 JavaScript 对象是否包含键 (obj[key] !== undefined) 并在函数返回 true (key is keyof typeof obj) 时告诉 TypeScript 类型。现在,如果我们使用它进行测试,TypeScript 知道当您执行 obj[key] 时它在“if”的第一个分支中有一个数字,我认为这就是您所要求的。不满意之处在于,我们不得不编写一个新的、困难的函数来做一些琐碎的事情,并且我们已经检索了 obj[key] 两次,只是为了在一个常量对象上获得类型安全。

const obj = {
    a: 1,
    b: 2,
    c: 3
};

let key: string = Math.random() < 0.5 ? 'a' : 'anything';

function isObjKey(key: string): key is keyof typeof obj {
    return obj.hasOwnProperty(key);
}

if (isObjKey(key)) {
    const d = obj[key];
    // d is a number here as far as TypeScript is concerned
    console.log(d);
}
else {
    // key isn't a valid key of obj, there's no need for another obj[key] access
    // (but if you do it's type any, not undefined)
    console.log('undefined');
}

另一种方法就是破解它。你向 TypeScript 断言你的 key 肯定是 obj 的 key,即使它可能不是,并且 TypeScript 认为它在 obj[key] 之后有一个数字:

const d = obj[key as keyof typeof obj];
if (d) {
    // TypeScript thinks d is a number
    console.log(d);
} else {
    // TypeScript still thinks d is a number, but it probably doesn't matter!
    console.log('undefined');
}

【讨论】:

  • 我不确定它是如何令人不满意的,因为在您的示例中,键绝对可能不是 obj 的键。检查它是任何好的代码都应该做的。如果“任何东西”是关键,它应该怎么做?这不是钥匙
  • 谢谢!你的第一个例子我几乎明白了。现在的问题是除了值的实际类型之外,打字稿说它可以是undefined。我知道实际上该对象可能在运行时发生了变化,但是有没有办法告诉 TS 它不能是undefined? (如果用Object.freeze定义,我们知道它不能改变)Playground link
  • 首先,我意识到如果打开 noImplicitAny,我的答案将不起作用,我已经对其进行了编辑以解决该问题。我已将 obj[key] !=== undefined 替换为 obj.hasOwnProperty(key)
  • 我不认为这是你的问题吗?但是,我不明白您的问题是什么:您是说 d 显示为 'number | 'if' 语句中的 undefined'(我在这里写了 'd 是一个数字')?因为那不是我看到的。问题到底出在哪里(以及哪个变量?)?
【解决方案2】:

我没听错吗?

const obj: Record<string, any> = { ... };

const getter = (key: string): string => {
    if (key in obj) return typeof obj[key];
    return undefined;
}

【讨论】:

  • 这不适用于--strictNullChecks,请参阅here。没有--strictNullChecks 就没有undefined 类型需要担心,但是问题不会要求number | undefined 类型。
猜你喜欢
  • 2017-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-23
  • 2016-09-14
  • 1970-01-01
相关资源
最近更新 更多